estela 0.1.40 → 0.2.1

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 (293) hide show
  1. package/README.md +46 -2
  2. package/dist/billing/assign.d.ts +45 -0
  3. package/dist/billing/assign.js +159 -0
  4. package/dist/bin.js +23 -1
  5. package/dist/cli.js +220 -57
  6. package/dist/db/store.d.ts +2 -0
  7. package/dist/db/store.js +24 -1
  8. package/dist/demo.js +1 -1
  9. package/dist/i18n/en.js +39 -0
  10. package/dist/metrics/quota.d.ts +44 -4
  11. package/dist/metrics/quota.js +60 -14
  12. package/dist/project-of.d.ts +52 -0
  13. package/dist/project-of.js +124 -0
  14. package/dist/quota.d.ts +10 -1
  15. package/dist/quota.js +14 -20
  16. package/dist/server.js +10 -27
  17. package/dist/statusline-cache.d.ts +35 -0
  18. package/dist/statusline-cache.js +57 -0
  19. package/dist/statusline-install.d.ts +52 -0
  20. package/dist/statusline-install.js +115 -0
  21. package/dist/statusline-run.d.ts +8 -0
  22. package/dist/statusline-run.js +256 -0
  23. package/dist/statusline.d.ts +71 -0
  24. package/dist/statusline.js +152 -0
  25. package/dist/watchers/claude.d.ts +11 -1
  26. package/dist/watchers/claude.js +32 -15
  27. package/package.json +3 -1
  28. package/dist/billing/amortize.d.ts.map +0 -1
  29. package/dist/billing/amortize.js.map +0 -1
  30. package/dist/billing/amortize.test.d.ts +0 -2
  31. package/dist/billing/amortize.test.d.ts.map +0 -1
  32. package/dist/billing/amortize.test.js +0 -121
  33. package/dist/billing/amortize.test.js.map +0 -1
  34. package/dist/billing/format.test.d.ts +0 -2
  35. package/dist/billing/format.test.d.ts.map +0 -1
  36. package/dist/billing/format.test.js +0 -110
  37. package/dist/billing/format.test.js.map +0 -1
  38. package/dist/billing/invoice.d.ts.map +0 -1
  39. package/dist/billing/invoice.js.map +0 -1
  40. package/dist/billing/invoice.test.d.ts +0 -2
  41. package/dist/billing/invoice.test.d.ts.map +0 -1
  42. package/dist/billing/invoice.test.js +0 -200
  43. package/dist/billing/invoice.test.js.map +0 -1
  44. package/dist/billing/localize.d.ts.map +0 -1
  45. package/dist/billing/localize.js.map +0 -1
  46. package/dist/billing/localize.test.d.ts +0 -2
  47. package/dist/billing/localize.test.d.ts.map +0 -1
  48. package/dist/billing/localize.test.js +0 -144
  49. package/dist/billing/localize.test.js.map +0 -1
  50. package/dist/billing/sessionize.d.ts.map +0 -1
  51. package/dist/billing/sessionize.js.map +0 -1
  52. package/dist/billing/sessionize.test.d.ts +0 -2
  53. package/dist/billing/sessionize.test.d.ts.map +0 -1
  54. package/dist/billing/sessionize.test.js +0 -187
  55. package/dist/billing/sessionize.test.js.map +0 -1
  56. package/dist/billing.d.ts.map +0 -1
  57. package/dist/billing.js.map +0 -1
  58. package/dist/bin.d.ts.map +0 -1
  59. package/dist/bin.js.map +0 -1
  60. package/dist/cli-badge.test.d.ts +0 -2
  61. package/dist/cli-badge.test.d.ts.map +0 -1
  62. package/dist/cli-badge.test.js +0 -118
  63. package/dist/cli-badge.test.js.map +0 -1
  64. package/dist/cli-idioma.test.d.ts +0 -2
  65. package/dist/cli-idioma.test.d.ts.map +0 -1
  66. package/dist/cli-idioma.test.js +0 -190
  67. package/dist/cli-idioma.test.js.map +0 -1
  68. package/dist/cli-quota.test.d.ts +0 -2
  69. package/dist/cli-quota.test.d.ts.map +0 -1
  70. package/dist/cli-quota.test.js +0 -216
  71. package/dist/cli-quota.test.js.map +0 -1
  72. package/dist/cli.d.ts.map +0 -1
  73. package/dist/cli.js.map +0 -1
  74. package/dist/cloud/auth.d.ts.map +0 -1
  75. package/dist/cloud/auth.js.map +0 -1
  76. package/dist/cloud/client.d.ts.map +0 -1
  77. package/dist/cloud/client.js.map +0 -1
  78. package/dist/cloud/errors.test.d.ts +0 -2
  79. package/dist/cloud/errors.test.d.ts.map +0 -1
  80. package/dist/cloud/errors.test.js +0 -34
  81. package/dist/cloud/errors.test.js.map +0 -1
  82. package/dist/db/schema.d.ts.map +0 -1
  83. package/dist/db/schema.js.map +0 -1
  84. package/dist/db/schema.test.d.ts +0 -2
  85. package/dist/db/schema.test.d.ts.map +0 -1
  86. package/dist/db/schema.test.js +0 -825
  87. package/dist/db/schema.test.js.map +0 -1
  88. package/dist/db/store.d.ts.map +0 -1
  89. package/dist/db/store.js.map +0 -1
  90. package/dist/demo.d.ts.map +0 -1
  91. package/dist/demo.js.map +0 -1
  92. package/dist/demo.test.d.ts +0 -2
  93. package/dist/demo.test.d.ts.map +0 -1
  94. package/dist/demo.test.js +0 -148
  95. package/dist/demo.test.js.map +0 -1
  96. package/dist/doctor.d.ts.map +0 -1
  97. package/dist/doctor.js.map +0 -1
  98. package/dist/doctor.test.d.ts +0 -2
  99. package/dist/doctor.test.d.ts.map +0 -1
  100. package/dist/doctor.test.js +0 -167
  101. package/dist/doctor.test.js.map +0 -1
  102. package/dist/export/badge.d.ts.map +0 -1
  103. package/dist/export/badge.js.map +0 -1
  104. package/dist/export/badge.test.d.ts +0 -2
  105. package/dist/export/badge.test.d.ts.map +0 -1
  106. package/dist/export/badge.test.js +0 -32
  107. package/dist/export/badge.test.js.map +0 -1
  108. package/dist/export/clockify.d.ts.map +0 -1
  109. package/dist/export/clockify.js.map +0 -1
  110. package/dist/export/clockify.test.d.ts +0 -2
  111. package/dist/export/clockify.test.d.ts.map +0 -1
  112. package/dist/export/clockify.test.js +0 -51
  113. package/dist/export/clockify.test.js.map +0 -1
  114. package/dist/export/csv.d.ts.map +0 -1
  115. package/dist/export/csv.js.map +0 -1
  116. package/dist/export/dates.d.ts.map +0 -1
  117. package/dist/export/dates.js.map +0 -1
  118. package/dist/export/idioma.test.d.ts +0 -2
  119. package/dist/export/idioma.test.d.ts.map +0 -1
  120. package/dist/export/idioma.test.js +0 -255
  121. package/dist/export/idioma.test.js.map +0 -1
  122. package/dist/export/invoice-pdf.d.ts.map +0 -1
  123. package/dist/export/invoice-pdf.js.map +0 -1
  124. package/dist/export/panel.d.ts.map +0 -1
  125. package/dist/export/panel.js.map +0 -1
  126. package/dist/export/panel.test.d.ts +0 -2
  127. package/dist/export/panel.test.d.ts.map +0 -1
  128. package/dist/export/panel.test.js +0 -386
  129. package/dist/export/panel.test.js.map +0 -1
  130. package/dist/export/pdf.d.ts.map +0 -1
  131. package/dist/export/pdf.js.map +0 -1
  132. package/dist/export/pdf.test.d.ts +0 -2
  133. package/dist/export/pdf.test.d.ts.map +0 -1
  134. package/dist/export/pdf.test.js +0 -172
  135. package/dist/export/pdf.test.js.map +0 -1
  136. package/dist/export/pixelfont.d.ts.map +0 -1
  137. package/dist/export/pixelfont.js.map +0 -1
  138. package/dist/export/png.d.ts.map +0 -1
  139. package/dist/export/png.js.map +0 -1
  140. package/dist/export/receipt.d.ts.map +0 -1
  141. package/dist/export/receipt.js.map +0 -1
  142. package/dist/export/receipt.test.d.ts +0 -2
  143. package/dist/export/receipt.test.d.ts.map +0 -1
  144. package/dist/export/receipt.test.js +0 -207
  145. package/dist/export/receipt.test.js.map +0 -1
  146. package/dist/export/share.d.ts.map +0 -1
  147. package/dist/export/share.js.map +0 -1
  148. package/dist/export/share.test.d.ts +0 -2
  149. package/dist/export/share.test.d.ts.map +0 -1
  150. package/dist/export/share.test.js +0 -116
  151. package/dist/export/share.test.js.map +0 -1
  152. package/dist/i18n/en.d.ts.map +0 -1
  153. package/dist/i18n/en.js.map +0 -1
  154. package/dist/i18n/i18n.test.d.ts +0 -2
  155. package/dist/i18n/i18n.test.d.ts.map +0 -1
  156. package/dist/i18n/i18n.test.js +0 -226
  157. package/dist/i18n/i18n.test.js.map +0 -1
  158. package/dist/i18n/index.d.ts.map +0 -1
  159. package/dist/i18n/index.js.map +0 -1
  160. package/dist/i18n/labels.d.ts.map +0 -1
  161. package/dist/i18n/labels.js.map +0 -1
  162. package/dist/metrics/index.d.ts.map +0 -1
  163. package/dist/metrics/index.js.map +0 -1
  164. package/dist/metrics/metrics.test.d.ts +0 -2
  165. package/dist/metrics/metrics.test.d.ts.map +0 -1
  166. package/dist/metrics/metrics.test.js +0 -162
  167. package/dist/metrics/metrics.test.js.map +0 -1
  168. package/dist/metrics/people.d.ts.map +0 -1
  169. package/dist/metrics/people.js.map +0 -1
  170. package/dist/metrics/people.test.d.ts +0 -2
  171. package/dist/metrics/people.test.d.ts.map +0 -1
  172. package/dist/metrics/people.test.js +0 -92
  173. package/dist/metrics/people.test.js.map +0 -1
  174. package/dist/metrics/quota.d.ts.map +0 -1
  175. package/dist/metrics/quota.js.map +0 -1
  176. package/dist/metrics/quota.test.d.ts +0 -2
  177. package/dist/metrics/quota.test.d.ts.map +0 -1
  178. package/dist/metrics/quota.test.js +0 -262
  179. package/dist/metrics/quota.test.js.map +0 -1
  180. package/dist/metrics/team.d.ts.map +0 -1
  181. package/dist/metrics/team.js.map +0 -1
  182. package/dist/metrics/team.test.d.ts +0 -2
  183. package/dist/metrics/team.test.d.ts.map +0 -1
  184. package/dist/metrics/team.test.js +0 -90
  185. package/dist/metrics/team.test.js.map +0 -1
  186. package/dist/node-version.d.ts.map +0 -1
  187. package/dist/node-version.js.map +0 -1
  188. package/dist/node-version.test.d.ts +0 -2
  189. package/dist/node-version.test.d.ts.map +0 -1
  190. package/dist/node-version.test.js +0 -53
  191. package/dist/node-version.test.js.map +0 -1
  192. package/dist/paths.d.ts.map +0 -1
  193. package/dist/paths.js.map +0 -1
  194. package/dist/paths.test.d.ts +0 -2
  195. package/dist/paths.test.d.ts.map +0 -1
  196. package/dist/paths.test.js +0 -32
  197. package/dist/paths.test.js.map +0 -1
  198. package/dist/pricing/catalog.d.ts.map +0 -1
  199. package/dist/pricing/catalog.js.map +0 -1
  200. package/dist/pricing/cost.d.ts.map +0 -1
  201. package/dist/pricing/cost.js.map +0 -1
  202. package/dist/pricing/cost.test.d.ts +0 -2
  203. package/dist/pricing/cost.test.d.ts.map +0 -1
  204. package/dist/pricing/cost.test.js +0 -100
  205. package/dist/pricing/cost.test.js.map +0 -1
  206. package/dist/publish.d.ts.map +0 -1
  207. package/dist/publish.js.map +0 -1
  208. package/dist/publish.test.d.ts +0 -2
  209. package/dist/publish.test.d.ts.map +0 -1
  210. package/dist/publish.test.js +0 -113
  211. package/dist/publish.test.js.map +0 -1
  212. package/dist/quota.d.ts.map +0 -1
  213. package/dist/quota.js.map +0 -1
  214. package/dist/server.d.ts.map +0 -1
  215. package/dist/server.js.map +0 -1
  216. package/dist/server.test.d.ts +0 -2
  217. package/dist/server.test.d.ts.map +0 -1
  218. package/dist/server.test.js +0 -402
  219. package/dist/server.test.js.map +0 -1
  220. package/dist/setup.d.ts.map +0 -1
  221. package/dist/setup.js.map +0 -1
  222. package/dist/setup.test.d.ts +0 -2
  223. package/dist/setup.test.d.ts.map +0 -1
  224. package/dist/setup.test.js +0 -144
  225. package/dist/setup.test.js.map +0 -1
  226. package/dist/sync/team.d.ts.map +0 -1
  227. package/dist/sync/team.js.map +0 -1
  228. package/dist/sync/team.test.d.ts +0 -2
  229. package/dist/sync/team.test.d.ts.map +0 -1
  230. package/dist/sync/team.test.js +0 -102
  231. package/dist/sync/team.test.js.map +0 -1
  232. package/dist/sync.d.ts.map +0 -1
  233. package/dist/sync.js.map +0 -1
  234. package/dist/sync.test.d.ts +0 -2
  235. package/dist/sync.test.d.ts.map +0 -1
  236. package/dist/sync.test.js +0 -151
  237. package/dist/sync.test.js.map +0 -1
  238. package/dist/team.d.ts.map +0 -1
  239. package/dist/team.js.map +0 -1
  240. package/dist/team.test.d.ts +0 -2
  241. package/dist/team.test.d.ts.map +0 -1
  242. package/dist/team.test.js +0 -163
  243. package/dist/team.test.js.map +0 -1
  244. package/dist/updateCheck.d.ts.map +0 -1
  245. package/dist/updateCheck.js.map +0 -1
  246. package/dist/updateCheck.test.d.ts +0 -2
  247. package/dist/updateCheck.test.d.ts.map +0 -1
  248. package/dist/updateCheck.test.js +0 -27
  249. package/dist/updateCheck.test.js.map +0 -1
  250. package/dist/watchers/agents.d.ts.map +0 -1
  251. package/dist/watchers/agents.js.map +0 -1
  252. package/dist/watchers/agents.test.d.ts +0 -2
  253. package/dist/watchers/agents.test.d.ts.map +0 -1
  254. package/dist/watchers/agents.test.js +0 -96
  255. package/dist/watchers/agents.test.js.map +0 -1
  256. package/dist/watchers/claude.d.ts.map +0 -1
  257. package/dist/watchers/claude.js.map +0 -1
  258. package/dist/watchers/claude.test.d.ts +0 -2
  259. package/dist/watchers/claude.test.d.ts.map +0 -1
  260. package/dist/watchers/claude.test.js +0 -180
  261. package/dist/watchers/claude.test.js.map +0 -1
  262. package/dist/watchers/codex.d.ts.map +0 -1
  263. package/dist/watchers/codex.js.map +0 -1
  264. package/dist/watchers/codex.test.d.ts +0 -2
  265. package/dist/watchers/codex.test.d.ts.map +0 -1
  266. package/dist/watchers/codex.test.js +0 -207
  267. package/dist/watchers/codex.test.js.map +0 -1
  268. package/dist/watchers/copilot.d.ts.map +0 -1
  269. package/dist/watchers/copilot.js.map +0 -1
  270. package/dist/watchers/copilot.test.d.ts +0 -2
  271. package/dist/watchers/copilot.test.d.ts.map +0 -1
  272. package/dist/watchers/copilot.test.js +0 -98
  273. package/dist/watchers/copilot.test.js.map +0 -1
  274. package/dist/watchers/git.d.ts.map +0 -1
  275. package/dist/watchers/git.js.map +0 -1
  276. package/dist/watchers/git.test.d.ts +0 -2
  277. package/dist/watchers/git.test.d.ts.map +0 -1
  278. package/dist/watchers/git.test.js +0 -75
  279. package/dist/watchers/git.test.js.map +0 -1
  280. package/dist/watchers/identity.d.ts.map +0 -1
  281. package/dist/watchers/identity.js.map +0 -1
  282. package/dist/watchers/identity.test.d.ts +0 -2
  283. package/dist/watchers/identity.test.d.ts.map +0 -1
  284. package/dist/watchers/identity.test.js +0 -48
  285. package/dist/watchers/identity.test.js.map +0 -1
  286. package/dist/web-ui.test.d.ts +0 -2
  287. package/dist/web-ui.test.d.ts.map +0 -1
  288. package/dist/web-ui.test.js +0 -414
  289. package/dist/web-ui.test.js.map +0 -1
  290. package/dist/workitems.test.d.ts +0 -2
  291. package/dist/workitems.test.d.ts.map +0 -1
  292. package/dist/workitems.test.js +0 -59
  293. package/dist/workitems.test.js.map +0 -1
package/README.md CHANGED
@@ -116,6 +116,11 @@ Two things trip people up before Estela even starts:
116
116
  the standalone Node, then `nvm install lts` and `nvm use lts` from an
117
117
  administrator terminal. `where.exe npm` should list a single path.
118
118
 
119
+ A session in a subfolder or a git worktree counts for its repository's
120
+ project. If an import would add hours to what you had already imported, Estela
121
+ shows you how many per project and asks first (`estela import --yes` skips the
122
+ question); it never moves hours between projects or touches an approved day.
123
+
119
124
  ## Reporting to a client
120
125
 
121
126
  Projects are created as internal and without a rate, because making one up
@@ -238,8 +243,9 @@ expect it and nothing tells you where it went. `estela quota` splits it by
238
243
  project:
239
244
 
240
245
  ```sh
241
- estela quota # current 5-hour window and this week, per project
242
- estela quota --json # the same, for scripts
246
+ estela quota # current 5-hour window and this week, per project
247
+ estela quota --weighted # the % by list price instead of by tokens
248
+ estela quota --json # the same, for scripts
243
249
  ```
244
250
 
245
251
  Tokens (input, output, cache read and cache write) per project, for the current
@@ -248,6 +254,13 @@ week against the average of your previous 4 over the same stretch. Claude Code
248
254
  and Codex are shown separately: they draw on different quotas. It's also a card
249
255
  in the Summary tab of `estela web`, and it's free.
250
256
 
257
+ A session in a subfolder or in a git worktree counts for its repo's project;
258
+ a folder that isn't a project shows up under its git root's name. The % is
259
+ by tokens by default; `--weighted` splits it by each model's list price
260
+ instead, which is not how the provider counts your limit. With the status
261
+ line installed (below), the 5-hour window is the one Claude Code reports
262
+ rather than an estimate.
263
+
251
264
  What it never does is tell you how much you have left: Anthropic and OpenAI
252
265
  don't publish their plans' limits, so any "X tokens left" would be made up. The
253
266
  5-hour window is an estimate — how its start is rounded isn't public, and
@@ -255,6 +268,37 @@ anything you use on claude.ai or another machine counts too but isn't on this
255
268
  one. Copilot is left out because it doesn't record reliable tokens. Everything
256
269
  is calculated on your machine.
257
270
 
271
+ ## A line under every Claude Code session
272
+
273
+ ```sh
274
+ npm install -g estela # the status line needs estela on your PATH
275
+ estela statusline install
276
+ ```
277
+
278
+ In a repository that isn't a project yet it shows `estela · web (no project) ·
279
+ estela setup`, and outside git just `estela · no project` — plus the week and
280
+ 5-hour figures when Claude Code sends them. Each Claude Code
281
+ session in a project shows a line like this:
282
+
283
+ ```
284
+ estela · Brightside Health › brightside-portal · today 2h14 · this project 21% · week 38% · 5h 62%
285
+ ```
286
+
287
+ Hours measured today on this project (the same number the report will
288
+ give), this project's share of your Claude Code usage this week, and — only
289
+ if Claude Code sends them — its own weekly and 5-hour limit percentages, as
290
+ they come. Estela never estimates a limit or counts down to a reset. The
291
+ 5-hour figure turns amber from 80%. On a narrow terminal the line drops the
292
+ 5-hour figure first, then the week, then shortens the name, then this
293
+ project, then today; `estela` always stays. It runs in about 50 ms and only
294
+ reads what's new in the current session's transcript.
295
+
296
+ `install` merges the block into `~/.claude/settings.json` without touching
297
+ anything else, after making a backup. If you already have a status line it
298
+ won't overwrite it: `--force` replaces it, `--append` shows yours after
299
+ Estela's. `estela statusline uninstall` removes it (and puts yours back if
300
+ you used `--append`).
301
+
258
302
  ## Hours no import will infer
259
303
 
260
304
  Meetings, travel, research, and development without an agent that didn't leave
@@ -0,0 +1,45 @@
1
+ import type { DatabaseSync } from "node:sqlite";
2
+ import type { TimeEntry } from "@estela/shared";
3
+ import { type WorkBlock } from "./sessionize.js";
4
+ /**
5
+ * De bloques de trabajo a imputaciones: a qué proyecto va cada uno y con qué
6
+ * id se guarda. Lo usan `estela import` y el import automático del panel, que
7
+ * antes lo tenían copiado cada uno por su lado.
8
+ *
9
+ * La raíz git (worktrees, subcarpetas de un repo sin la carpeta exacta
10
+ * registrada) recupera horas que antes se perdían, con tres reglas:
11
+ *
12
+ * 1. Solo se reasigna lo que hoy NO tiene proyecto. La carpeta tal cual se
13
+ * mira primero, así que nada de lo ya asignado cambia de proyecto.
14
+ * 2. Un día aprobado o facturado de ese proyecto no se toca: las horas que se
15
+ * recuperarían ahí se informan (`blocked`), no se aplican.
16
+ * 3. Recuperar es opcional (`recover`): la primera vez lo confirma la persona.
17
+ */
18
+ export interface PlannedEntry {
19
+ readonly entry: Omit<TimeEntry, "id"> & {
20
+ id: string;
21
+ };
22
+ /** Segundos que esta imputación gana por la raíz git (0 si no cambia). */
23
+ readonly recoveredSeconds: number;
24
+ }
25
+ export interface RecoveryBlocked {
26
+ readonly projectId: string;
27
+ readonly day: string;
28
+ readonly seconds: number;
29
+ }
30
+ export interface AssignmentPlan {
31
+ readonly entries: readonly PlannedEntry[];
32
+ /** Bloques tras agrupar por rama y día, con y sin proyecto. */
33
+ readonly grouped: number;
34
+ /** De esos, los que siguen sin proyecto. */
35
+ readonly unassigned: number;
36
+ /** Segundos recuperados que SÍ se aplican, por proyecto. */
37
+ readonly recovered: ReadonlyMap<string, number>;
38
+ /** Lo que se recuperaría en días aprobados o facturados: no se aplica. */
39
+ readonly blocked: readonly RecoveryBlocked[];
40
+ }
41
+ export declare function entryId(projectId: string, day: string, branch: string | null): string;
42
+ export declare function planEntries(db: DatabaseSync, raw: readonly WorkBlock[], options: {
43
+ readonly recover: boolean;
44
+ }): AssignmentPlan;
45
+ //# sourceMappingURL=assign.d.ts.map
@@ -0,0 +1,159 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.entryId = entryId;
37
+ exports.planEntries = planEntries;
38
+ const shared_1 = require("@estela/shared");
39
+ const store = __importStar(require("../db/store.js"));
40
+ const project_of_js_1 = require("../project-of.js");
41
+ const sessionize_js_1 = require("./sessionize.js");
42
+ function entryId(projectId, day, branch) {
43
+ // Determinista (proyecto + día + rama) para que reimportar actualice en vez
44
+ // de duplicar. La fecha, la LOCAL: la misma que usa el servidor.
45
+ const slug = (branch ?? "sin-rama").replace(/[^a-zA-Z0-9]+/g, "-");
46
+ return `te_${projectId}_${day}_${slug}`;
47
+ }
48
+ function planEntries(db, raw, options) {
49
+ const ownerOf = (0, project_of_js_1.ownerResolver)(db);
50
+ const direct = (repo) => (repo ? store.projectForRepo(db, repo) : null);
51
+ const viaRoot = (repo) => {
52
+ if (!repo)
53
+ return null;
54
+ const o = ownerOf(repo);
55
+ return o.kind === "project" ? o.projectId : null;
56
+ };
57
+ // Lo de siempre: la carpeta tal cual. Se agrupa por PROYECTO, no por ruta:
58
+ // `repo/` y `repo/src/UI` son el mismo proyecto, y agrupar por ruta daba dos
59
+ // bloques que luego se pisaban al guardarse con el mismo id.
60
+ const keyDirect = (b) => direct(b.repoPath) ?? b.repoPath ?? "";
61
+ const groupedBase = (0, sessionize_js_1.groupByBranchAndDay)(raw, keyDirect);
62
+ const base = byId(groupedBase, direct);
63
+ if (!options.recover) {
64
+ return {
65
+ entries: [...base.values()].map((g) => ({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: 0 })),
66
+ grouped: groupedBase.length,
67
+ unassigned: groupedBase.filter((b) => !direct(b.repoPath)).length,
68
+ recovered: new Map(), blocked: [],
69
+ };
70
+ }
71
+ // Con la raíz git, pero solo para lo que no tenía proyecto (regla 1).
72
+ const assigned = (repo) => direct(repo) ?? viaRoot(repo);
73
+ const keyFull = (b) => assigned(b.repoPath) ?? b.repoPath ?? "";
74
+ const groupedFull = (0, sessionize_js_1.groupByBranchAndDay)(raw, keyFull);
75
+ const full = byId(groupedFull, assigned);
76
+ // Qué imputaciones cambian: las que reciben algún bloque recuperado.
77
+ const touched = new Set();
78
+ for (const b of raw) {
79
+ if (direct(b.repoPath))
80
+ continue;
81
+ const p = viaRoot(b.repoPath);
82
+ if (p)
83
+ touched.add(entryId(p, (0, shared_1.localDate)(b.startedAt), b.branch));
84
+ }
85
+ const lockedDays = new Map();
86
+ const locked = (projectId, day) => {
87
+ const k = `${projectId}\0${day}`;
88
+ if (!lockedDays.has(k)) {
89
+ lockedDays.set(k, !!db.prepare(`SELECT 1 FROM time_entries WHERE project_id = ? AND local_date = ?
90
+ AND (approved = 1 OR invoice_id IS NOT NULL) LIMIT 1`).get(projectId, day));
91
+ }
92
+ return lockedDays.get(k);
93
+ };
94
+ const entries = [];
95
+ const recovered = new Map();
96
+ const blocked = [];
97
+ for (const [id, g] of full) {
98
+ const before = base.get(id);
99
+ if (!touched.has(id)) {
100
+ entries.push({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: 0 });
101
+ continue;
102
+ }
103
+ const gain = Math.max(0, g.block.seconds - (before?.block.seconds ?? 0));
104
+ const day = (0, shared_1.localDate)(g.block.startedAt);
105
+ if (locked(g.projectId, day)) {
106
+ // Regla 2: el día se queda como estaba; si antes no había nada, nada.
107
+ if (before)
108
+ entries.push({ entry: toEntry(db, before.projectId, before.block), recoveredSeconds: 0 });
109
+ if (gain > 0)
110
+ blocked.push({ projectId: g.projectId, day, seconds: gain });
111
+ continue;
112
+ }
113
+ entries.push({ entry: toEntry(db, g.projectId, g.block), recoveredSeconds: gain });
114
+ if (gain > 0)
115
+ recovered.set(g.projectId, (recovered.get(g.projectId) ?? 0) + gain);
116
+ }
117
+ return {
118
+ entries, grouped: groupedFull.length,
119
+ unassigned: groupedFull.filter((b) => !assigned(b.repoPath)).length,
120
+ recovered, blocked,
121
+ };
122
+ }
123
+ /** Los bloques agrupados con proyecto, por el id con el que se guardarían. */
124
+ function byId(blocks, projectOf) {
125
+ const out = new Map();
126
+ for (const block of blocks) {
127
+ const projectId = projectOf(block.repoPath);
128
+ if (!projectId)
129
+ continue;
130
+ out.set(entryId(projectId, (0, shared_1.localDate)(block.startedAt), block.branch), { projectId, block });
131
+ }
132
+ return out;
133
+ }
134
+ function toEntry(db, projectId, block) {
135
+ const project = store.getProject(db, projectId);
136
+ return {
137
+ id: entryId(projectId, (0, shared_1.localDate)(block.startedAt), block.branch),
138
+ projectId,
139
+ startedAt: block.startedAt,
140
+ endedAt: block.endedAt,
141
+ seconds: block.seconds,
142
+ description: (0, sessionize_js_1.describeBlock)(block),
143
+ billable: project.billable,
144
+ invoiceId: null,
145
+ aiCost: block.aiCost,
146
+ // Un bloque deducido solo de commits no tuvo agente: ni segundos de
147
+ // agente ni agente al que atribuirlos.
148
+ agentSeconds: block.turnCount > 0 ? block.seconds : 0,
149
+ commitHashes: block.commits.map((c) => c.hash),
150
+ agents: block.turnCount > 0 ? [...(block.agents ?? ["claude-code"])] : [],
151
+ source: block.turnCount > 0 ? "agent" : "commit",
152
+ kind: "development",
153
+ branch: block.branch,
154
+ // El ticket se saca aquí: en la base solo quedan los hashes de los
155
+ // commits, y es en sus mensajes donde va el `AB#1234`.
156
+ workItems: (0, shared_1.extractWorkItems)(block.branch, block.commits.map((c) => c.subject), project.tracker),
157
+ };
158
+ }
159
+ //# sourceMappingURL=assign.js.map
package/dist/bin.js CHANGED
@@ -20,7 +20,29 @@ Object.defineProperty(exports, "__esModule", { value: true });
20
20
  const node_version_js_1 = require("./node-version.js");
21
21
  const index_js_1 = require("./i18n/index.js");
22
22
  const MIN_NODE = "22.5.0";
23
- if ((0, node_version_js_1.tooOld)(process.version, MIN_NODE)) {
23
+ // `estela statusline` (y no `statusline install|uninstall`, que son de la CLI):
24
+ // lo ejecuta Claude Code en cada evento de la sesión y tiene 100 ms. Va por un
25
+ // camino aparte que no carga cli.js, y en un Node viejo imprime "estela" y
26
+ // sale con 0 en vez de un error: la status line no se rompe nunca.
27
+ const sub = process.argv[3];
28
+ const esStatusline = process.argv[2] === "statusline" &&
29
+ !["install", "uninstall", "help", "--help"].includes(sub ?? "");
30
+ if (esStatusline) {
31
+ // El aviso de que node:sqlite es experimental iría a stderr en cada evento.
32
+ process.removeAllListeners("warning");
33
+ process.on("warning", () => { });
34
+ if ((0, node_version_js_1.tooOld)(process.version, MIN_NODE)) {
35
+ process.stdout.write("estela\n");
36
+ }
37
+ else {
38
+ const i = process.argv.indexOf("--lang");
39
+ (0, index_js_1.setLang)((0, index_js_1.detectLang)({ flag: i >= 0 ? process.argv[i + 1] : undefined, env: process.env, platform: process.platform }));
40
+ void import("./statusline-run.js")
41
+ .then((m) => m.runStatusline(process.argv.slice(3)))
42
+ .catch(() => { process.stdout.write("estela\n"); });
43
+ }
44
+ }
45
+ else if ((0, node_version_js_1.tooOld)(process.version, MIN_NODE)) {
24
46
  // Aquí aún no se ha leído ningún argumento: `--lang` se busca a mano.
25
47
  const i = process.argv.indexOf("--lang");
26
48
  (0, index_js_1.setLang)((0, index_js_1.detectLang)({ flag: i >= 0 ? process.argv[i + 1] : undefined, env: process.env, platform: process.platform }));