@knpkv/jira-clockify 1.2.1 → 1.4.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 (243) hide show
  1. package/README.md +254 -3
  2. package/dist/package.json +36 -12
  3. package/dist/src/agent/agentSettings.d.ts +18 -0
  4. package/dist/src/agent/agentSettings.d.ts.map +1 -0
  5. package/dist/src/agent/agentSettings.js +23 -0
  6. package/dist/src/agent/agentSettings.js.map +1 -0
  7. package/dist/src/agent/schedule.d.ts +20 -0
  8. package/dist/src/agent/schedule.d.ts.map +1 -0
  9. package/dist/src/agent/schedule.js +189 -0
  10. package/dist/src/agent/schedule.js.map +1 -0
  11. package/dist/src/agent/sessions.d.ts +427 -0
  12. package/dist/src/agent/sessions.d.ts.map +1 -0
  13. package/dist/src/agent/sessions.js +868 -0
  14. package/dist/src/agent/sessions.js.map +1 -0
  15. package/dist/src/agent/sourceConsumption.d.ts +79 -0
  16. package/dist/src/agent/sourceConsumption.d.ts.map +1 -0
  17. package/dist/src/agent/sourceConsumption.js +191 -0
  18. package/dist/src/agent/sourceConsumption.js.map +1 -0
  19. package/dist/src/agent/watch.d.ts +76 -0
  20. package/dist/src/agent/watch.d.ts.map +1 -0
  21. package/dist/src/agent/watch.js +45 -0
  22. package/dist/src/agent/watch.js.map +1 -0
  23. package/dist/src/agent/writePlanning.d.ts +154 -0
  24. package/dist/src/agent/writePlanning.d.ts.map +1 -0
  25. package/dist/src/agent/writePlanning.js +210 -0
  26. package/dist/src/agent/writePlanning.js.map +1 -0
  27. package/dist/src/bin.d.ts +3 -0
  28. package/dist/src/bin.d.ts.map +1 -0
  29. package/dist/src/bin.js +9 -3
  30. package/dist/src/bin.js.map +1 -1
  31. package/dist/src/cli/agentWrite.d.ts +164 -0
  32. package/dist/src/cli/agentWrite.d.ts.map +1 -0
  33. package/dist/src/cli/agentWrite.js +362 -0
  34. package/dist/src/cli/agentWrite.js.map +1 -0
  35. package/dist/src/cli/auth.d.ts +8 -0
  36. package/dist/src/cli/auth.d.ts.map +1 -0
  37. package/dist/src/cli/auth.js +10 -10
  38. package/dist/src/cli/auth.js.map +1 -1
  39. package/dist/src/cli/calendar.d.ts +42 -0
  40. package/dist/src/cli/calendar.d.ts.map +1 -0
  41. package/dist/src/cli/calendar.js +189 -0
  42. package/dist/src/cli/calendar.js.map +1 -0
  43. package/dist/src/cli/config.d.ts +12 -0
  44. package/dist/src/cli/config.d.ts.map +1 -0
  45. package/dist/src/cli/config.js +163 -21
  46. package/dist/src/cli/config.js.map +1 -1
  47. package/dist/src/cli/fetchTicket.d.ts +51 -0
  48. package/dist/src/cli/fetchTicket.d.ts.map +1 -0
  49. package/dist/src/cli/fetchTicket.js +18 -2
  50. package/dist/src/cli/fetchTicket.js.map +1 -1
  51. package/dist/src/cli/fuzzySelect.d.ts +24 -0
  52. package/dist/src/cli/fuzzySelect.d.ts.map +1 -0
  53. package/dist/src/cli/layers.d.ts +40 -0
  54. package/dist/src/cli/layers.d.ts.map +1 -0
  55. package/dist/src/cli/layers.js +56 -12
  56. package/dist/src/cli/layers.js.map +1 -1
  57. package/dist/src/cli/list.d.ts +8 -0
  58. package/dist/src/cli/list.d.ts.map +1 -0
  59. package/dist/src/cli/list.js +2 -2
  60. package/dist/src/cli/list.js.map +1 -1
  61. package/dist/src/cli/reconcile.d.ts +129 -0
  62. package/dist/src/cli/reconcile.d.ts.map +1 -0
  63. package/dist/src/cli/reconcile.js +635 -46
  64. package/dist/src/cli/reconcile.js.map +1 -1
  65. package/dist/src/cli/root.d.ts +15 -0
  66. package/dist/src/cli/root.d.ts.map +1 -0
  67. package/dist/src/cli/root.js +4 -3
  68. package/dist/src/cli/root.js.map +1 -1
  69. package/dist/src/cli/runtimeFailure.d.ts +10 -0
  70. package/dist/src/cli/runtimeFailure.d.ts.map +1 -0
  71. package/dist/src/cli/runtimeFailure.js +17 -0
  72. package/dist/src/cli/runtimeFailure.js.map +1 -0
  73. package/dist/src/cli/setup.d.ts +10 -0
  74. package/dist/src/cli/setup.d.ts.map +1 -0
  75. package/dist/src/cli/setup.js +7 -7
  76. package/dist/src/cli/setup.js.map +1 -1
  77. package/dist/src/cli/timer/discard.d.ts +4 -0
  78. package/dist/src/cli/timer/discard.d.ts.map +1 -0
  79. package/dist/src/cli/timer/discard.js +2 -2
  80. package/dist/src/cli/timer/discard.js.map +1 -1
  81. package/dist/src/cli/timer/edit.d.ts +22 -0
  82. package/dist/src/cli/timer/edit.d.ts.map +1 -0
  83. package/dist/src/cli/timer/edit.js +62 -42
  84. package/dist/src/cli/timer/edit.js.map +1 -1
  85. package/dist/src/cli/timer/index.d.ts +12 -0
  86. package/dist/src/cli/timer/index.d.ts.map +1 -0
  87. package/dist/src/cli/timer/log.d.ts +16 -0
  88. package/dist/src/cli/timer/log.d.ts.map +1 -0
  89. package/dist/src/cli/timer/log.js +6 -6
  90. package/dist/src/cli/timer/log.js.map +1 -1
  91. package/dist/src/cli/timer/start.d.ts +21 -0
  92. package/dist/src/cli/timer/start.d.ts.map +1 -0
  93. package/dist/src/cli/timer/start.js +9 -9
  94. package/dist/src/cli/timer/start.js.map +1 -1
  95. package/dist/src/cli/timer/status.d.ts +22 -0
  96. package/dist/src/cli/timer/status.d.ts.map +1 -0
  97. package/dist/src/cli/timer/status.js +110 -71
  98. package/dist/src/cli/timer/status.js.map +1 -1
  99. package/dist/src/cli/timer/stop.d.ts +48 -0
  100. package/dist/src/cli/timer/stop.d.ts.map +1 -0
  101. package/dist/src/cli/timer/stop.js +19 -19
  102. package/dist/src/cli/timer/stop.js.map +1 -1
  103. package/dist/src/cli/timer.d.ts +11 -0
  104. package/dist/src/cli/timer.d.ts.map +1 -0
  105. package/dist/src/cli/timer.js +1 -1
  106. package/dist/src/cli/timer.js.map +1 -1
  107. package/dist/src/cli/watch.d.ts +54 -0
  108. package/dist/src/cli/watch.d.ts.map +1 -0
  109. package/dist/src/cli/watch.js +412 -0
  110. package/dist/src/cli/watch.js.map +1 -0
  111. package/dist/src/cli/watchLease.d.ts +99 -0
  112. package/dist/src/cli/watchLease.d.ts.map +1 -0
  113. package/dist/src/cli/watchLease.js +204 -0
  114. package/dist/src/cli/watchLease.js.map +1 -0
  115. package/dist/src/cli/writerGuard.d.ts +37 -0
  116. package/dist/src/cli/writerGuard.d.ts.map +1 -0
  117. package/dist/src/cli/writerGuard.js +92 -0
  118. package/dist/src/cli/writerGuard.js.map +1 -0
  119. package/dist/src/index.d.ts +46 -0
  120. package/dist/src/index.d.ts.map +1 -0
  121. package/dist/src/index.js +46 -0
  122. package/dist/src/index.js.map +1 -0
  123. package/dist/src/main.d.ts +4 -0
  124. package/dist/src/main.d.ts.map +1 -0
  125. package/dist/src/services/AgentSessionReader.d.ts +121 -0
  126. package/dist/src/services/AgentSessionReader.d.ts.map +1 -0
  127. package/dist/src/services/AgentSessionReader.js +392 -0
  128. package/dist/src/services/AgentSessionReader.js.map +1 -0
  129. package/dist/src/services/ClockifyAuth.d.ts +47 -0
  130. package/dist/src/services/ClockifyAuth.d.ts.map +1 -0
  131. package/dist/src/services/ClockifyAuth.js +2 -2
  132. package/dist/src/services/ClockifyAuth.js.map +1 -1
  133. package/dist/src/services/CodexTranscript.d.ts +112 -0
  134. package/dist/src/services/CodexTranscript.d.ts.map +1 -0
  135. package/dist/src/services/CodexTranscript.js +96 -0
  136. package/dist/src/services/CodexTranscript.js.map +1 -0
  137. package/dist/src/services/ConfigService.d.ts +99 -0
  138. package/dist/src/services/ConfigService.d.ts.map +1 -0
  139. package/dist/src/services/ConfigService.js +79 -7
  140. package/dist/src/services/ConfigService.js.map +1 -1
  141. package/dist/src/services/HomeDirectory.d.ts +17 -0
  142. package/dist/src/services/HomeDirectory.d.ts.map +1 -0
  143. package/dist/src/services/HomeDirectory.js +1 -1
  144. package/dist/src/services/IssueFacts.d.ts +127 -0
  145. package/dist/src/services/IssueFacts.d.ts.map +1 -0
  146. package/dist/src/services/IssueFacts.js +277 -0
  147. package/dist/src/services/IssueFacts.js.map +1 -0
  148. package/dist/src/services/ProviderDeletion.d.ts +19 -0
  149. package/dist/src/services/ProviderDeletion.d.ts.map +1 -0
  150. package/dist/src/services/ProviderDeletion.js +37 -0
  151. package/dist/src/services/ProviderDeletion.js.map +1 -0
  152. package/dist/src/services/ReconcileService.d.ts +378 -0
  153. package/dist/src/services/ReconcileService.d.ts.map +1 -0
  154. package/dist/src/services/ReconcileService.js +1270 -79
  155. package/dist/src/services/ReconcileService.js.map +1 -1
  156. package/dist/src/services/SavedEntries.d.ts +40 -0
  157. package/dist/src/services/SavedEntries.d.ts.map +1 -0
  158. package/dist/src/services/SavedEntries.js +209 -0
  159. package/dist/src/services/SavedEntries.js.map +1 -0
  160. package/dist/src/services/SessionAttributor.d.ts +114 -0
  161. package/dist/src/services/SessionAttributor.d.ts.map +1 -0
  162. package/dist/src/services/SessionAttributor.js +240 -0
  163. package/dist/src/services/SessionAttributor.js.map +1 -0
  164. package/dist/src/services/SourceLedger.d.ts +104 -0
  165. package/dist/src/services/SourceLedger.d.ts.map +1 -0
  166. package/dist/src/services/SourceLedger.js +302 -0
  167. package/dist/src/services/SourceLedger.js.map +1 -0
  168. package/dist/src/services/StateWriter.d.ts +39 -0
  169. package/dist/src/services/StateWriter.d.ts.map +1 -0
  170. package/dist/src/services/TicketService.d.ts +63 -0
  171. package/dist/src/services/TicketService.d.ts.map +1 -0
  172. package/dist/src/services/TimerService.d.ts +145 -0
  173. package/dist/src/services/TimerService.d.ts.map +1 -0
  174. package/dist/src/services/TimerService.js +113 -80
  175. package/dist/src/services/TimerService.js.map +1 -1
  176. package/dist/src/services/internal/JiraWorklogPost.d.ts +7 -0
  177. package/dist/src/services/internal/JiraWorklogPost.d.ts.map +1 -0
  178. package/dist/src/services/internal/JiraWorklogPost.js +32 -0
  179. package/dist/src/services/internal/JiraWorklogPost.js.map +1 -0
  180. package/dist/src/testing/fakeHeadless.d.ts +318 -0
  181. package/dist/src/testing/fakeHeadless.d.ts.map +1 -0
  182. package/dist/src/testing/fakeHeadless.js +1149 -0
  183. package/dist/src/testing/fakeHeadless.js.map +1 -0
  184. package/dist/src/tui/App.d.ts +6 -0
  185. package/dist/src/tui/App.d.ts.map +1 -0
  186. package/dist/src/tui/App.js +1 -1
  187. package/dist/src/tui/App.js.map +1 -1
  188. package/dist/src/tui/atoms/runtime.d.ts +8 -0
  189. package/dist/src/tui/atoms/runtime.d.ts.map +1 -0
  190. package/dist/src/tui/atoms/runtime.js +1 -1
  191. package/dist/src/tui/atoms/runtime.js.map +1 -1
  192. package/dist/src/tui/atoms/tickets.d.ts +3 -0
  193. package/dist/src/tui/atoms/tickets.d.ts.map +1 -0
  194. package/dist/src/tui/atoms/timer.d.ts +11 -0
  195. package/dist/src/tui/atoms/timer.d.ts.map +1 -0
  196. package/dist/src/tui/atoms/timer.js +1 -1
  197. package/dist/src/tui/atoms/timer.js.map +1 -1
  198. package/dist/src/tui/atoms/ui.d.ts +11 -0
  199. package/dist/src/tui/atoms/ui.d.ts.map +1 -0
  200. package/dist/src/tui/atoms/ui.js +1 -1
  201. package/dist/src/tui/atoms/ui.js.map +1 -1
  202. package/dist/src/tui/components/BigTimer.d.ts +2 -0
  203. package/dist/src/tui/components/BigTimer.d.ts.map +1 -0
  204. package/dist/src/tui/components/Footer.d.ts +2 -0
  205. package/dist/src/tui/components/Footer.d.ts.map +1 -0
  206. package/dist/src/tui/components/Header.d.ts +2 -0
  207. package/dist/src/tui/components/Header.d.ts.map +1 -0
  208. package/dist/src/tui/components/Header.js +1 -1
  209. package/dist/src/tui/components/Header.js.map +1 -1
  210. package/dist/src/tui/components/PopupInput.d.ts +18 -0
  211. package/dist/src/tui/components/PopupInput.d.ts.map +1 -0
  212. package/dist/src/tui/components/PopupMessage.d.ts +20 -0
  213. package/dist/src/tui/components/PopupMessage.d.ts.map +1 -0
  214. package/dist/src/tui/components/TicketList.d.ts +2 -0
  215. package/dist/src/tui/components/TicketList.d.ts.map +1 -0
  216. package/dist/src/tui/components/TicketList.js +1 -1
  217. package/dist/src/tui/components/TicketList.js.map +1 -1
  218. package/dist/src/tui/components/TicketRow.d.ts +8 -0
  219. package/dist/src/tui/components/TicketRow.d.ts.map +1 -0
  220. package/dist/src/tui/components/TimerDisplay.d.ts +2 -0
  221. package/dist/src/tui/components/TimerDisplay.d.ts.map +1 -0
  222. package/dist/src/tui/components/index.d.ts +12 -0
  223. package/dist/src/tui/components/index.d.ts.map +1 -0
  224. package/dist/src/tui/context/theme.d.ts +15 -0
  225. package/dist/src/tui/context/theme.d.ts.map +1 -0
  226. package/dist/src/tui/hooks/useElapsedTimer.d.ts +6 -0
  227. package/dist/src/tui/hooks/useElapsedTimer.d.ts.map +1 -0
  228. package/dist/src/tui/hooks/useElapsedTimer.js +1 -1
  229. package/dist/src/tui/hooks/useElapsedTimer.js.map +1 -1
  230. package/dist/src/tui/hooks/useTerminalSize.d.ts +5 -0
  231. package/dist/src/tui/hooks/useTerminalSize.d.ts.map +1 -0
  232. package/dist/src/utils/hints.d.ts +12 -0
  233. package/dist/src/utils/hints.d.ts.map +1 -0
  234. package/dist/src/utils/hints.js +12 -0
  235. package/dist/src/utils/hints.js.map +1 -0
  236. package/dist/src/utils/time.d.ts +104 -0
  237. package/dist/src/utils/time.d.ts.map +1 -0
  238. package/dist/src/utils/time.js +74 -8
  239. package/dist/src/utils/time.js.map +1 -1
  240. package/dist/tsconfig.tsbuildinfo +1 -1
  241. package/nvim/lua/jcf/state.lua +191 -13
  242. package/package.json +36 -18
  243. package/skills/jcf/SKILL.md +93 -1
@@ -0,0 +1,412 @@
1
+ /**
2
+ * `jcf watch claude` — log Agent Session time as it happens, without being asked each time.
3
+ *
4
+ * **Mental model**
5
+ *
6
+ * - **`reconcile --agent` on a loop, with the picker removed.** Nothing new is derived here. A
7
+ * proposal is still `session − (already recorded)`, which is what makes a repeating writer safe:
8
+ * a failed write, a half-written row, or a laptop that slept all afternoon simply shows up as the
9
+ * same gap on the next tick.
10
+ * - **Writes what it can defend.** Unattended, it writes only blocks that have settled and only
11
+ * attributions a person deliberately created — a branch name, a worktree path, a Standing
12
+ * Attribution. Everything else is named on screen and left for `jcf sync reconcile --agent`.
13
+ * - **Forward only, bar one settle window.** The window opens a settle period before the watch
14
+ * does — the stretch nothing could have written yet, so a restart recovers the tail it was
15
+ * holding rather than dropping it. Nothing older: a command that writes hours you have not seen is
16
+ * the one thing worse than a command that writes nothing. Work older than that window is
17
+ * `reconcile`'s, which shows you the rows first.
18
+ *
19
+ * **Gotchas**
20
+ *
21
+ * - The interval is how often it *looks*, not how quickly a block is written. A block is written on
22
+ * the first tick after it settles, so the lag is up to one interval past the settle deadline.
23
+ * - Jira refusing the login or lacking a verified provider account stops the watch rather than
24
+ * retrying. Every later write would fail the same way, and a watch that logs to Clockify alone
25
+ * for six hours quietly recreates the discrepancy the tool exists to close.
26
+ *
27
+ * @module
28
+ */
29
+ import { Console, Data, Effect, Option, Runtime, Schedule } from "effect";
30
+ import { Argument as Args, Command, Flag as Options } from "effect/cli";
31
+ import * as Clock from "effect/Clock";
32
+ import * as Duration from "effect/Duration";
33
+ import { decideWatchWrites, SETTLE_GRACE_SECONDS } from "../agent/watch.js";
34
+ import { ConfigService } from "../services/ConfigService.js";
35
+ import { isOwnedByMe, IssueFacts } from "../services/IssueFacts.js";
36
+ import { ReconcileService } from "../services/ReconcileService.js";
37
+ import { formatClock, formatDuration } from "../utils/time.js";
38
+ import { applyProposal, clockifyWritten, entryDescription, jiraWritten, keepGoing, proposalTargets, writeOutcomeLines, writeStopReason } from "./agentWrite.js";
39
+ import { fetchTicketByKey } from "./fetchTicket.js";
40
+ import * as WatchLease from "./watchLease.js";
41
+ import * as WriterGuard from "./writerGuard.js";
42
+ /** Coding Agents whose sessions jcf can watch. Matches `reconcile --agent`. */
43
+ const SUPPORTED_AGENTS = ["claude"];
44
+ /**
45
+ * How often to look, in seconds.
46
+ *
47
+ * Deliberately much longer than it takes to read the transcripts. A block is not writable until it
48
+ * has settled anyway, so a faster poll buys no earlier write — only more work per hour to reach the
49
+ * same answer.
50
+ */
51
+ const DEFAULT_INTERVAL_SECONDS = 300;
52
+ /** Wrong arguments, not a failed operation — fails the command so a script sees a non-zero exit. */
53
+ export class WatchUsageError extends Data.TaggedError("WatchUsageError") {
54
+ [Runtime.errorReported] = false;
55
+ }
56
+ /**
57
+ * A cadence as a person would say it: `5m`, not `5m 0s`.
58
+ *
59
+ * `formatDuration` always spells the seconds out because it reports *worked* time, where the odd
60
+ * seconds are the point. An interval is a round number nobody wants read back to them.
61
+ */
62
+ /** How much a day's unplaced time must grow before the watch names it again. */
63
+ const UNPLACED_ANNOUNCE_STEP_SECONDS = 30 * 60;
64
+ const cadence = (seconds) => formatDuration(seconds).replace(/ 0s$/, "");
65
+ /** How a held row reads on screen — one line, saying what would unblock it. */
66
+ const heldLine = (held) => {
67
+ const when = held.reason._tag === "Unsettled"
68
+ ? `settles at ${formatClock(new Date(held.reason.settlesAtMs))}`
69
+ : "needs review — run jcf sync reconcile --agent claude";
70
+ return ` ${held.proposal.ticketKey.padEnd(12)} ${proposalTargets(held.proposal).padEnd(17)} ${when}`;
71
+ };
72
+ /** The bucket a held or reported row belongs to, for saying a thing once rather than every tick. */
73
+ const rowKey = (row) => `${row.ticketKey}\u0000${row.day}`;
74
+ /**
75
+ * What makes a dry-run preview distinct from one already shown.
76
+ *
77
+ * The bucket alone is not enough: a ticket worked again after lunch produces a genuinely different
78
+ * proposal for the same `(key, day)`, and that one deserves its own line. The deltas change whenever
79
+ * there is anything new to say, so they belong in the key.
80
+ */
81
+ const previewKey = (proposal) => `${rowKey(proposal)}\u0000${proposal.clockifyDelta}\u0000${proposal.jiraDelta}`;
82
+ export const runWatch = (options) => Effect.scoped(Effect.gen(function* () {
83
+ const svc = yield* ReconcileService;
84
+ const config = yield* ConfigService;
85
+ const issues = yield* IssueFacts;
86
+ const cfg = yield* config.get;
87
+ const startedAtMs = yield* Clock.currentTimeMillis;
88
+ const settleSeconds = cfg.sessionIdleCapSeconds + SETTLE_GRACE_SECONDS;
89
+ const totals = { blocks: 0, clockifySeconds: 0, jiraSeconds: 0 };
90
+ // Said once per bucket, not once per tick: a ticket that cannot be placed would otherwise
91
+ // repeat its own line every interval until the watch is killed.
92
+ const announced = new Set();
93
+ if (cfg.sessionRoots.length === 0) {
94
+ yield* Console.log("Nothing is opted in yet. Add a Session Root: jcf config set session-root <dir>");
95
+ return;
96
+ }
97
+ // One writer at a time. Subtracting what the sides hold makes a later tick safe and says nothing
98
+ // about a simultaneous one: two watches can read the same gap before either writes it.
99
+ let unresolvedFromMs = startedAtMs;
100
+ const lease = yield* Effect.acquireRelease(WatchLease.acquire({ intervalSeconds: options.intervalSeconds }), (lease) => lease._tag === "Held"
101
+ ? Effect.suspend(() => WatchLease.release({
102
+ path: lease.path,
103
+ owner: lease.owner,
104
+ heldSinceMs: startedAtMs,
105
+ intervalSeconds: options.intervalSeconds,
106
+ unresolvedFromMs
107
+ }))
108
+ : Effect.void);
109
+ if (lease._tag === "Taken") {
110
+ yield* Console.log(`Another jcf watch has been running since ${formatClock(new Date(lease.sinceMs))}.` +
111
+ " Two would write the same hours twice, so this one is stopping.");
112
+ return;
113
+ }
114
+ if (lease._tag === "Unavailable") {
115
+ // Not the same as losing the race, and not something to shrug off: with no lease on disk there
116
+ // is nothing to stop a second watch deriving the same gap and writing it a second time.
117
+ yield* Console.error(`Cannot take the watch lease — ${lease.reason}.`);
118
+ return yield* new WatchUsageError({ message: `Cannot take the watch lease: ${lease.reason}` });
119
+ }
120
+ const leasePath = lease.path;
121
+ const leaseOwner = lease.owner;
122
+ const watchFromMs = lease.resumeFromMs ?? startedAtMs;
123
+ unresolvedFromMs = watchFromMs;
124
+ const guarded = Effect.gen(function* () {
125
+ /**
126
+ * Where this run starts looking.
127
+ *
128
+ * The cursor alone, with no floor of its own. Taking `max(cursor, now − settleWindow)` looked
129
+ * safer and was the opposite: an old cursor then resolved to `now − settleWindow`, so every
130
+ * restart back-dated a fresh window of unreviewed work. The lease decides whether a resume is
131
+ * offered at all — only when the previous holder stopped recently — so by the time there is a
132
+ * cursor here, reaching to it is exactly resuming what that run was already holding.
133
+ */
134
+ yield* Console.log(`jcf watch ${options.agent}${options.dryRun ? " (dry run — writes nothing)" : ""}`);
135
+ yield* Console.log(` Looking every ${cadence(options.intervalSeconds)}, from ${formatClock(new Date(watchFromMs))}.` +
136
+ ` A block is written once it has been quiet for ${cadence(settleSeconds)}.`);
137
+ yield* Console.log(" Only branch-, path-, and standing-attributed work is written. Earlier work and anything" +
138
+ " needing review: jcf sync reconcile --agent claude");
139
+ const carryOn = { _tag: "Continue" };
140
+ /**
141
+ * One pass: derive, decide, describe, write.
142
+ *
143
+ * The window starts where the watch did, so the recorded side is measured over exactly the
144
+ * span the session side is — a Clockify entry from before the watch began belongs to neither.
145
+ */
146
+ // The earliest instant this run has not resolved. Seeded at the window's start and moved forward
147
+ // only past work that is settled and dealt with, so a run stopped mid-block leaves the block's
148
+ // own start behind rather than the moment it happened to stop — resuming from the latter would
149
+ // filter out the very prompts that made the block unsettled.
150
+ /**
151
+ * Say we are still here, and stop if we are not.
152
+ *
153
+ * Called at the top of a tick and again immediately before writing. The lease is immutable while
154
+ * held, but verifying its owner still fails closed if the file is removed, replaced, unreadable,
155
+ * or malformed before a provider write.
156
+ */
157
+ const stillMine = Effect.gen(function* () {
158
+ const standing = yield* WatchLease.refresh({ path: leasePath, owner: leaseOwner });
159
+ return standing._tag === "Mine"
160
+ ? null
161
+ : { _tag: "Stop", reason: `${standing.reason}, so this one is standing down.` };
162
+ });
163
+ const tick = Effect.gen(function* () {
164
+ const nowMs = yield* Clock.currentTimeMillis;
165
+ const held = yield* stillMine;
166
+ if (held !== null)
167
+ return held;
168
+ const period = { from: new Date(watchFromMs), to: new Date(nowMs) };
169
+ const report = yield* svc.proposeFromSessions(period, { attribution: "deterministic" }).pipe(
170
+ // A tick that cannot read is a tick that writes nothing. The next one re-derives from
171
+ // scratch, so a transient failure costs a delay rather than an hour.
172
+ Effect.catch((error) => Console.error(` Could not read sessions: ${error.message}`).pipe(Effect.as(null))));
173
+ if (report === null)
174
+ return carryOn;
175
+ for (const excluded of report.excludedDays) {
176
+ const key = `excluded\u0000${excluded.day}`;
177
+ if (announced.has(key))
178
+ continue;
179
+ announced.add(key);
180
+ yield* Console.log(` ${excluded.day} skipped — ${excluded.reason}`);
181
+ }
182
+ for (const entry of report.unattributed) {
183
+ // Said again when the day's unplaced time grows materially: a later session on the same
184
+ // day, or the total crossing another half hour. Raw seconds would repeat on every tick
185
+ // while one session's credit is still growing.
186
+ const growth = Math.floor(entry.seconds / UNPLACED_ANNOUNCE_STEP_SECONDS);
187
+ const key = `unattributed\u0000${entry.day}\u0000${entry.sessionCount}\u0000${growth}`;
188
+ if (announced.has(key))
189
+ continue;
190
+ announced.add(key);
191
+ yield* Console.log(` ${formatDuration(entry.seconds)} on ${entry.day} that no branch, path, or standing rule places` +
192
+ ` — jcf sync reconcile --agent ${options.agent}`);
193
+ }
194
+ const decision = decideWatchWrites(report.proposals, { nowMs });
195
+ const excludedDays = new Set(report.excludedDays.map(({ day }) => day));
196
+ const excludedFrom = report.attributed
197
+ .filter(({ day }) => excludedDays.has(day))
198
+ .map(({ sourceStartMs }) => sourceStartMs);
199
+ /**
200
+ * Move the cursor to the earliest instant this tick is finished with.
201
+ *
202
+ * Two things hold it back, and both must, because the cursor is what a restart trusts. An
203
+ * unsettled block can still grow, so its own start stays behind. And a *settled* block is not
204
+ * finished with either until both sides that were short have taken it: this used to advance
205
+ * here, before the writes below, so a Jira refusal after Clockify had succeeded persisted a
206
+ * cursor past the block — and the restart the message asks for then filtered out the very row
207
+ * whose Jira half was missing. `--dry-run` is the same shape without the failure: it writes
208
+ * nothing, so it must leave nothing behind.
209
+ *
210
+ * With nothing held at all, the settle horizon is the boundary. Never moves backwards, so a
211
+ * quiet look cannot forget a block an earlier one was holding.
212
+ */
213
+ const commitCursor = (unwritten) => {
214
+ const heldFrom = decision.held
215
+ .filter((entry) => entry.reason._tag === "Unsettled")
216
+ .map((entry) => entry.proposal.sourceStartMs);
217
+ // A running timer removes its day from proposals, but not from the source evidence this
218
+ // watch owns. Keep that evidence behind the cursor so stopping the timer and restarting
219
+ // can still derive it instead of treating the skipped day as resolved.
220
+ const pendingFrom = [...heldFrom, ...unwritten, ...excludedFrom];
221
+ const horizon = pendingFrom.length === 0
222
+ ? nowMs - settleSeconds * 1000
223
+ : Math.min(...pendingFrom);
224
+ unresolvedFromMs = Math.max(unresolvedFromMs, Math.min(horizon, nowMs));
225
+ };
226
+ /** The earliest instant a proposal covers — where the cursor must stop if it is not written. */
227
+ const proposalStart = (proposal) => proposal.sourceStartMs;
228
+ for (const held of decision.held) {
229
+ if (held.reason._tag !== "NeedsReview")
230
+ continue;
231
+ const key = `review\u0000${rowKey(held.proposal)}`;
232
+ if (announced.has(key))
233
+ continue;
234
+ announced.add(key);
235
+ yield* Console.log(heldLine(held));
236
+ }
237
+ const readOwnership = (proposals) => Effect.gen(function* () {
238
+ const settings = yield* config.get;
239
+ const facts = settings.sessionOwnership === "assigned"
240
+ ? (yield* issues.lookup(proposals.map((proposal) => proposal.ticketKey))).facts
241
+ : new Map();
242
+ return {
243
+ facts,
244
+ mode: settings.sessionOwnership,
245
+ overrides: settings.sessionOwnershipOverrides
246
+ };
247
+ });
248
+ const ownership = yield* readOwnership(decision.write);
249
+ const permitted = decision.write.filter((proposal) => isOwnedByMe(proposal.ticketKey, ownership));
250
+ for (const proposal of decision.write) {
251
+ if (isOwnedByMe(proposal.ticketKey, ownership))
252
+ continue;
253
+ const key = `owner\u0000${rowKey(proposal)}`;
254
+ if (announced.has(key))
255
+ continue;
256
+ announced.add(key);
257
+ const fact = ownership.facts.get(proposal.ticketKey);
258
+ const owner = fact?.assignee === null ? "unassigned" : `assigned to ${fact?.assignee ?? "somebody else"}`;
259
+ yield* Console.log(` ${formatDuration(proposal.sessionSeconds)} on ${proposal.day} for ${proposal.ticketKey}` +
260
+ ` — ${owner}; not written unattended`);
261
+ }
262
+ // Under `--dry-run` nothing is written, so a settled row stays settled and would be described
263
+ // and re-printed on every single tick — hundreds of paid Coding Agent calls for one unchanging
264
+ // preview if left running overnight. Each distinct row is previewed once.
265
+ const pending = options.dryRun
266
+ ? permitted.filter((proposal) => !announced.has(`preview\u0000${previewKey(proposal)}`))
267
+ : permitted;
268
+ if (options.dryRun) {
269
+ for (const proposal of pending)
270
+ announced.add(`preview\u0000${previewKey(proposal)}`);
271
+ }
272
+ // Every settled proposal starts out unwritten, and only a confirmed write clears one. A dry run
273
+ // clears none — including a row it previewed on an earlier tick and is skipping now, which is
274
+ // why this covers `decision.write` rather than `pending`.
275
+ const unwritten = new Set(decision.write);
276
+ const unwrittenStarts = () => [...unwritten].map(proposalStart);
277
+ if (pending.length === 0) {
278
+ commitCursor(unwrittenStarts());
279
+ return carryOn;
280
+ }
281
+ // Looked up per write rather than cached for the run: a watch outlives the facts, and an issue
282
+ // renamed at lunchtime should read correctly on the entry written after it.
283
+ const summaries = new Map();
284
+ for (const proposal of pending) {
285
+ if (summaries.has(proposal.ticketKey))
286
+ continue;
287
+ const found = yield* fetchTicketByKey(proposal.ticketKey);
288
+ if (found._tag === "Found")
289
+ summaries.set(proposal.ticketKey, found.ticket.summary);
290
+ }
291
+ const notes = yield* svc.describeProposals({
292
+ proposals: pending,
293
+ digests: report.digests,
294
+ summaries
295
+ });
296
+ // Summary lookups and description generation are unbounded. Re-tally afterwards so another
297
+ // local surface cannot fill a gap, or start a timer, while this tick is waiting and still have
298
+ // the old proposal written.
299
+ const refreshed = options.dryRun
300
+ ? report
301
+ : yield* svc.refreshRecordedTime(period, report).pipe(Effect.catch((error) => Console.error(` Could not refresh providers before writing: ${error.message}`).pipe(Effect.as(null))));
302
+ if (refreshed === null) {
303
+ commitCursor(unwrittenStarts());
304
+ return carryOn;
305
+ }
306
+ // Re-checked here rather than only at the top of the tick: everything since then — the session
307
+ // read, a ticket lookup per key, a Coding Agent call — is unbounded, and this is the last
308
+ // moment before anything is written.
309
+ if (!options.dryRun) {
310
+ const stillHeld = yield* stillMine;
311
+ if (stillHeld !== null) {
312
+ commitCursor(unwrittenStarts());
313
+ return stillHeld;
314
+ }
315
+ }
316
+ // Ownership and overrides can change while descriptions or provider refreshes are running.
317
+ // Re-read them at the final write boundary just as the lease itself is re-checked there.
318
+ const currentOwnership = yield* readOwnership(pending);
319
+ for (const [index, original] of pending.entries()) {
320
+ if (!isOwnedByMe(original.ticketKey, currentOwnership)) {
321
+ const fact = currentOwnership.facts.get(original.ticketKey);
322
+ const owner = fact?.assignee === null
323
+ ? "unassigned"
324
+ : `assigned to ${fact?.assignee ?? "somebody else"}`;
325
+ yield* Console.log(` ${formatClock(new Date(nowMs))} ${original.ticketKey} skipped — ${owner}`);
326
+ continue;
327
+ }
328
+ const excluded = refreshed.excludedDays.find(({ day }) => day === original.day);
329
+ const proposal = refreshed.proposals.find((candidate) => candidate.ticketKey === original.ticketKey && candidate.day === original.day);
330
+ if (excluded !== undefined) {
331
+ yield* Console.log(` ${formatClock(new Date(nowMs))} ${original.ticketKey} skipped — ${excluded.reason}`);
332
+ continue;
333
+ }
334
+ if (proposal === undefined) {
335
+ unwritten.delete(original);
336
+ yield* Console.log(` ${formatClock(new Date(nowMs))} ${original.ticketKey} already logged — nothing written`);
337
+ continue;
338
+ }
339
+ const description = entryDescription({
340
+ summary: summaries.get(original.ticketKey) ?? null,
341
+ note: notes[index] ?? null
342
+ });
343
+ yield* Console.log(` ${formatClock(new Date(nowMs))} ${proposal.ticketKey} ${proposalTargets(proposal)}`);
344
+ yield* Console.log(` ${description}`);
345
+ if (options.dryRun) {
346
+ yield* Console.log(" · dry run — not written");
347
+ continue;
348
+ }
349
+ // Only what each side actually took. A Clockify failure or a Jira refusal still returns
350
+ // here, and a closing summary that claimed hours neither system holds would be wrong in the
351
+ // one direction this command must never be wrong in.
352
+ const written = yield* applyProposal(svc, proposal, description, refreshed.sourceScopes ?? {
353
+ clockify: null,
354
+ jira: null
355
+ }, undefined, refreshed.jiraAvailability);
356
+ yield* Effect.forEach(writeOutcomeLines(written), (line) => Console.log(` ${line}`));
357
+ const clockifySeconds = clockifyWritten(written);
358
+ const jiraSeconds = jiraWritten(written);
359
+ if (clockifySeconds > 0 || jiraSeconds > 0)
360
+ totals.blocks += 1;
361
+ totals.clockifySeconds += clockifySeconds;
362
+ totals.jiraSeconds += jiraSeconds;
363
+ // Only when both sides that were short have taken it. A half-written block stays behind the
364
+ // cursor, so the restart re-derives it and offers the missing side alone — the subtraction
365
+ // that makes every tick safe is the same thing that stops the written side repeating.
366
+ if (clockifySeconds >= proposal.clockifyDelta && jiraSeconds >= proposal.jiraDelta) {
367
+ unwritten.delete(original);
368
+ }
369
+ if (!keepGoing(written)) {
370
+ commitCursor(unwrittenStarts());
371
+ return {
372
+ _tag: "Stop",
373
+ reason: writeStopReason(written) ?? "Jira refused every remaining write."
374
+ };
375
+ }
376
+ }
377
+ commitCursor(unwrittenStarts());
378
+ return carryOn;
379
+ });
380
+ const loop = tick.pipe(Effect.repeat({
381
+ schedule: Schedule.spaced(Duration.seconds(options.intervalSeconds)),
382
+ until: (outcome) => outcome._tag === "Stop"
383
+ }), Effect.flatMap((outcome) => outcome._tag === "Stop" ? Console.log(` Stopped: ${outcome.reason}`) : Effect.void));
384
+ // Printed however the watch ends, including Ctrl-C: a run that wrote nothing and a run that was
385
+ // never working look identical otherwise.
386
+ yield* loop.pipe(Effect.ensuring(Effect.suspend(() => Console.log(totals.blocks === 0
387
+ ? " Wrote nothing this run."
388
+ : ` Wrote ${totals.blocks} block(s): Clockify ${formatDuration(totals.clockifySeconds)},` +
389
+ ` Jira ${formatDuration(totals.jiraSeconds)}.`))));
390
+ });
391
+ return yield* WriterGuard.withExistingWriterGuard(lease, guarded);
392
+ }));
393
+ export const watch = Command.make("watch", {
394
+ agent: Args.String("agent").pipe(Args.optional),
395
+ interval: Options.Int("interval").pipe(Options.withDescription(`Seconds between looks (default: ${DEFAULT_INTERVAL_SECONDS})`), Options.optional),
396
+ dryRun: Options.Boolean("dry-run").pipe(Options.withDescription("Report what would be written without creating anything"), Options.withDefault(false))
397
+ }, ({ agent, dryRun, interval }) => Effect.gen(function* () {
398
+ const name = Option.isSome(agent) ? agent.value : "claude";
399
+ if (!SUPPORTED_AGENTS.includes(name)) {
400
+ const message = `Unsupported agent "${name}". Supported: ${SUPPORTED_AGENTS.join(", ")}.`;
401
+ yield* Console.error(message);
402
+ return yield* new WatchUsageError({ message });
403
+ }
404
+ const intervalSeconds = Option.isSome(interval) ? interval.value : DEFAULT_INTERVAL_SECONDS;
405
+ if (intervalSeconds < 1) {
406
+ const message = "--interval must be at least 1 second.";
407
+ yield* Console.error(message);
408
+ return yield* new WatchUsageError({ message });
409
+ }
410
+ return yield* runWatch({ agent: name, intervalSeconds, dryRun });
411
+ })).pipe(Command.withDescription("Watch a Coding Agent's sessions and log settled work as it happens"));
412
+ //# sourceMappingURL=watch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watch.js","sourceRoot":"","sources":["../../../src/cli/watch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,99 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as FileSystem from "effect/FileSystem";
3
+ import * as Path from "effect/Path";
4
+ import { ConfigService } from "../services/ConfigService.js";
5
+ /** Whether this process may write, and what the last one left behind. */
6
+ export type LeaseOutcome = {
7
+ readonly _tag: "Held";
8
+ readonly path: string;
9
+ readonly owner: string;
10
+ /** Where to resume, or null when there is nothing legitimate to resume. */
11
+ readonly resumeFromMs: number | null;
12
+ } | {
13
+ readonly _tag: "Taken";
14
+ readonly sinceMs: number;
15
+ }
16
+ /** No lease could be written, so nothing is protecting a write. Distinct from losing the race. */
17
+ | {
18
+ readonly _tag: "Unavailable";
19
+ readonly reason: string;
20
+ };
21
+ /** Whether this process still holds what it took. */
22
+ export type LeaseStanding = {
23
+ readonly _tag: "Mine";
24
+ } | {
25
+ readonly _tag: "Lost";
26
+ readonly reason: string;
27
+ } | {
28
+ readonly _tag: "Unavailable";
29
+ readonly reason: string;
30
+ };
31
+ /**
32
+ * Take the lease, unless another watch holds it.
33
+ *
34
+ * The happy path is a single exclusive create: two watches starting together cannot both win it.
35
+ * An existing file is never overwritten. A stale-looking timestamp cannot make a read-then-write
36
+ * takeover atomic, so an ungraceful crash requires explicit human cleanup.
37
+ */
38
+ export declare const acquire: (options: {
39
+ readonly intervalSeconds: number;
40
+ }) => Effect.Effect<{
41
+ _tag: "Held";
42
+ path: string;
43
+ owner: string;
44
+ resumeFromMs: number | null;
45
+ reason?: never;
46
+ sinceMs?: never;
47
+ } | {
48
+ path?: never;
49
+ owner?: never;
50
+ resumeFromMs?: never;
51
+ _tag: "Unavailable";
52
+ reason: string;
53
+ sinceMs?: never;
54
+ } | {
55
+ path?: never;
56
+ owner?: never;
57
+ resumeFromMs?: never;
58
+ reason?: never;
59
+ _tag: "Taken";
60
+ sinceMs: number;
61
+ }, never, ConfigService | FileSystem.FileSystem | Path.Path>;
62
+ /**
63
+ * Verify that this process still owns the immutable lease.
64
+ *
65
+ * The cursor is persisted only on orderly release. Rewriting the lock while held would reintroduce
66
+ * a write race and buys no exclusion: ownership is the unchanged token on disk.
67
+ */
68
+ export declare const refresh: (options: {
69
+ readonly path: string;
70
+ readonly owner: string;
71
+ }) => Effect.Effect<{
72
+ readonly _tag: "Unavailable";
73
+ readonly reason: string;
74
+ } | {
75
+ reason?: never;
76
+ _tag: "Mine";
77
+ } | {
78
+ _tag: "Lost";
79
+ reason: string;
80
+ }, never, FileSystem.FileSystem>;
81
+ /**
82
+ * Stop holding the lease, keeping the cursor in a separate file.
83
+ *
84
+ * Silent when the lease is no longer this run's. A displaced process must not remove another
85
+ * owner's lock or publish its cursor as the next run's resume boundary.
86
+ */
87
+ export declare const release: (options: {
88
+ readonly path: string;
89
+ readonly owner: string;
90
+ readonly heldSinceMs: number;
91
+ readonly intervalSeconds: number;
92
+ readonly unresolvedFromMs: number;
93
+ }) => Effect.Effect<void, never, FileSystem.FileSystem | Path.Path>;
94
+ /** Release a short-lived non-watch writer without publishing a watch resume cursor. */
95
+ export declare const releaseGuard: (options: {
96
+ readonly path: string;
97
+ readonly owner: string;
98
+ }) => Effect.Effect<void, never, FileSystem.FileSystem>;
99
+ //# sourceMappingURL=watchLease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watchLease.d.ts","sourceRoot":"","sources":["../../../src/cli/watchLease.ts"],"names":[],"mappings":"AAkCA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,UAAU,MAAM,mBAAmB,CAAA;AAE/C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AAGnC,OAAO,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AA0B5D,yEAAyE;AACzE,MAAM,MAAM,YAAY,GACpB;IACA,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,2EAA2E;IAC3E,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;CACrC,GACC;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE;AACtD,kGAAkG;GAChG;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAE7D,qDAAqD;AACrD,MAAM,MAAM,aAAa,GACrB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GACzB;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AA8B7D;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,YAAa;IAAE,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;CAAE;;;;;;;;;;;;;;;;;;;;;4DAwEjE,CAAA;AAgCJ;;;;;GAKG;AACH,eAAO,MAAM,OAAO,YAAa;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE;mBA/B7D,aAAa;qBACX,MAAM;;;;;;;gCA+Bc,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,OAAO,YAAa;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;IAC5B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAA;IAChC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAA;CAClC,kEA4BG,CAAA;AAEJ,uFAAuF;AACvF,eAAO,MAAM,YAAY,YAAa;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE,sDAMnF,CAAA"}