project-logbook 1.2.0 → 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 (226) hide show
  1. package/README.md +2 -0
  2. package/dist/commands/answer.d.ts +1 -0
  3. package/dist/commands/answer.js +41 -0
  4. package/dist/commands/answer.js.map +1 -0
  5. package/dist/commands/answers.d.ts +6 -0
  6. package/dist/commands/answers.js +37 -0
  7. package/dist/commands/answers.js.map +1 -0
  8. package/dist/commands/ask.d.ts +1 -0
  9. package/dist/commands/ask.js +30 -0
  10. package/dist/commands/ask.js.map +1 -0
  11. package/dist/commands/build.js +25 -6
  12. package/dist/commands/build.js.map +1 -1
  13. package/dist/commands/deprecate-qna.d.ts +1 -0
  14. package/dist/commands/deprecate-qna.js +33 -0
  15. package/dist/commands/deprecate-qna.js.map +1 -0
  16. package/dist/commands/log.js +2 -0
  17. package/dist/commands/log.js.map +1 -1
  18. package/dist/commands/new-helpers.d.ts +0 -1
  19. package/dist/commands/new-helpers.js +6 -3
  20. package/dist/commands/new-helpers.js.map +1 -1
  21. package/dist/commands/release.js +2 -0
  22. package/dist/commands/release.js.map +1 -1
  23. package/dist/commands/start.js +90 -21
  24. package/dist/commands/start.js.map +1 -1
  25. package/dist/commands/status.js +6 -0
  26. package/dist/commands/status.js.map +1 -1
  27. package/dist/commands/unanswered.d.ts +1 -0
  28. package/dist/commands/unanswered.js +25 -0
  29. package/dist/commands/unanswered.js.map +1 -0
  30. package/dist/commands/watch.d.ts +15 -0
  31. package/dist/commands/watch.js +153 -0
  32. package/dist/commands/watch.js.map +1 -0
  33. package/dist/index.js +44 -0
  34. package/dist/index.js.map +1 -1
  35. package/dist/lib/autolink.d.ts +5 -0
  36. package/dist/lib/autolink.js +36 -2
  37. package/dist/lib/autolink.js.map +1 -1
  38. package/dist/lib/build-helpers.d.ts +10 -1
  39. package/dist/lib/build-helpers.js +27 -4
  40. package/dist/lib/build-helpers.js.map +1 -1
  41. package/dist/lib/build-steps.js +2 -1
  42. package/dist/lib/build-steps.js.map +1 -1
  43. package/dist/lib/config.d.ts +16 -0
  44. package/dist/lib/config.js +17 -0
  45. package/dist/lib/config.js.map +1 -1
  46. package/dist/lib/custom-card-renderer.js +2 -1
  47. package/dist/lib/custom-card-renderer.js.map +1 -1
  48. package/dist/lib/entry-href.d.ts +7 -0
  49. package/dist/lib/entry-href.js +8 -0
  50. package/dist/lib/entry-href.js.map +1 -0
  51. package/dist/lib/markdown-processors.d.ts +2 -1
  52. package/dist/lib/post-template.js +3 -3
  53. package/dist/lib/post-template.js.map +1 -1
  54. package/dist/lib/template-helpers.d.ts +2 -1
  55. package/dist/lib/template-helpers.js +32 -2
  56. package/dist/lib/template-helpers.js.map +1 -1
  57. package/dist/lib/template-types.d.ts +14 -0
  58. package/dist/lib/templates.js +14 -4
  59. package/dist/lib/templates.js.map +1 -1
  60. package/dist/linters/index.js +2 -0
  61. package/dist/linters/index.js.map +1 -1
  62. package/dist/linters/jira-prefix.js +32 -16
  63. package/dist/linters/jira-prefix.js.map +1 -1
  64. package/dist/linters/qna.d.ts +3 -0
  65. package/dist/linters/qna.js +120 -0
  66. package/dist/linters/qna.js.map +1 -0
  67. package/dist/templates/about-watch.md +29 -0
  68. package/dist/templates/hub.css +677 -0
  69. package/dist/templates/hub.js +743 -0
  70. package/dist/templates/qna.md +11 -0
  71. package/dist/templates/steer.txt +2 -0
  72. package/dist/templates/styles.css +53 -0
  73. package/dist/utils/id.d.ts +1 -1
  74. package/dist/utils/id.js +4 -1
  75. package/dist/utils/id.js.map +1 -1
  76. package/dist/utils/qna.d.ts +46 -0
  77. package/dist/utils/qna.js +157 -0
  78. package/dist/utils/qna.js.map +1 -0
  79. package/dist/watch/__tests__/git-utils.test.d.ts +1 -0
  80. package/dist/watch/__tests__/git-utils.test.js +17 -0
  81. package/dist/watch/__tests__/git-utils.test.js.map +1 -0
  82. package/dist/watch/collector.d.ts +14 -0
  83. package/dist/watch/collector.js +33 -0
  84. package/dist/watch/collector.js.map +1 -0
  85. package/dist/watch/collectors/acli.d.ts +15 -0
  86. package/dist/watch/collectors/acli.js +197 -0
  87. package/dist/watch/collectors/acli.js.map +1 -0
  88. package/dist/watch/collectors/entries.d.ts +9 -0
  89. package/dist/watch/collectors/entries.js +90 -0
  90. package/dist/watch/collectors/entries.js.map +1 -0
  91. package/dist/watch/collectors/git.d.ts +12 -0
  92. package/dist/watch/collectors/git.js +58 -0
  93. package/dist/watch/collectors/git.js.map +1 -0
  94. package/dist/watch/collectors/glab.d.ts +10 -0
  95. package/dist/watch/collectors/glab.js +273 -0
  96. package/dist/watch/collectors/glab.js.map +1 -0
  97. package/dist/watch/collectors/index.d.ts +10 -0
  98. package/dist/watch/collectors/index.js +21 -0
  99. package/dist/watch/collectors/index.js.map +1 -0
  100. package/dist/watch/collectors/jira.d.ts +3 -0
  101. package/dist/watch/collectors/jira.js +111 -0
  102. package/dist/watch/collectors/jira.js.map +1 -0
  103. package/dist/watch/collectors/type-guards.d.ts +17 -0
  104. package/dist/watch/collectors/type-guards.js +35 -0
  105. package/dist/watch/collectors/type-guards.js.map +1 -0
  106. package/dist/watch/collectors/types.d.ts +37 -0
  107. package/dist/watch/collectors/types.js +9 -0
  108. package/dist/watch/collectors/types.js.map +1 -0
  109. package/dist/watch/dashboard-html.d.ts +11 -0
  110. package/dist/watch/dashboard-html.js +81 -0
  111. package/dist/watch/dashboard-html.js.map +1 -0
  112. package/dist/watch/dashboard-script.d.ts +12 -0
  113. package/dist/watch/dashboard-script.js +20 -0
  114. package/dist/watch/dashboard-script.js.map +1 -0
  115. package/dist/watch/dashboard-styles.d.ts +12 -0
  116. package/dist/watch/dashboard-styles.js +20 -0
  117. package/dist/watch/dashboard-styles.js.map +1 -0
  118. package/dist/watch/dashboard.d.ts +12 -0
  119. package/dist/watch/dashboard.js +50 -0
  120. package/dist/watch/dashboard.js.map +1 -0
  121. package/dist/watch/git-utils.d.ts +36 -0
  122. package/dist/watch/git-utils.js +117 -0
  123. package/dist/watch/git-utils.js.map +1 -0
  124. package/dist/watch/hub-state.d.ts +39 -0
  125. package/dist/watch/hub-state.js +57 -0
  126. package/dist/watch/hub-state.js.map +1 -0
  127. package/dist/watch/hub.d.ts +2 -0
  128. package/dist/watch/hub.js +252 -0
  129. package/dist/watch/hub.js.map +1 -0
  130. package/dist/watch/log-utils.d.ts +13 -0
  131. package/dist/watch/log-utils.js +24 -0
  132. package/dist/watch/log-utils.js.map +1 -0
  133. package/dist/watch/notify.d.ts +18 -0
  134. package/dist/watch/notify.js +45 -0
  135. package/dist/watch/notify.js.map +1 -0
  136. package/dist/watch/state.d.ts +38 -0
  137. package/dist/watch/state.js +36 -0
  138. package/dist/watch/state.js.map +1 -0
  139. package/dist/watch/strategies/behind-default.d.ts +5 -0
  140. package/dist/watch/strategies/behind-default.js +21 -0
  141. package/dist/watch/strategies/behind-default.js.map +1 -0
  142. package/dist/watch/strategies/idle.d.ts +5 -0
  143. package/dist/watch/strategies/idle.js +21 -0
  144. package/dist/watch/strategies/idle.js.map +1 -0
  145. package/dist/watch/strategies/in-progress.d.ts +5 -0
  146. package/dist/watch/strategies/in-progress.js +21 -0
  147. package/dist/watch/strategies/in-progress.js.map +1 -0
  148. package/dist/watch/strategies/index.d.ts +34 -0
  149. package/dist/watch/strategies/index.js +148 -0
  150. package/dist/watch/strategies/index.js.map +1 -0
  151. package/dist/watch/strategies/jira-not-in-review.d.ts +2 -0
  152. package/dist/watch/strategies/jira-not-in-review.js +29 -0
  153. package/dist/watch/strategies/jira-not-in-review.js.map +1 -0
  154. package/dist/watch/strategies/jira-still-open.d.ts +2 -0
  155. package/dist/watch/strategies/jira-still-open.js +31 -0
  156. package/dist/watch/strategies/jira-still-open.js.map +1 -0
  157. package/dist/watch/strategies/mr-approval-optional.d.ts +5 -0
  158. package/dist/watch/strategies/mr-approval-optional.js +21 -0
  159. package/dist/watch/strategies/mr-approval-optional.js.map +1 -0
  160. package/dist/watch/strategies/mr-approved.d.ts +5 -0
  161. package/dist/watch/strategies/mr-approved.js +21 -0
  162. package/dist/watch/strategies/mr-approved.js.map +1 -0
  163. package/dist/watch/strategies/mr-conflicts.d.ts +5 -0
  164. package/dist/watch/strategies/mr-conflicts.js +21 -0
  165. package/dist/watch/strategies/mr-conflicts.js.map +1 -0
  166. package/dist/watch/strategies/mr-draft.d.ts +5 -0
  167. package/dist/watch/strategies/mr-draft.js +21 -0
  168. package/dist/watch/strategies/mr-draft.js.map +1 -0
  169. package/dist/watch/strategies/mr-open-comments.d.ts +5 -0
  170. package/dist/watch/strategies/mr-open-comments.js +21 -0
  171. package/dist/watch/strategies/mr-open-comments.js.map +1 -0
  172. package/dist/watch/strategies/narrative-missing.d.ts +5 -0
  173. package/dist/watch/strategies/narrative-missing.js +21 -0
  174. package/dist/watch/strategies/narrative-missing.js.map +1 -0
  175. package/dist/watch/strategies/no-mr.d.ts +5 -0
  176. package/dist/watch/strategies/no-mr.js +21 -0
  177. package/dist/watch/strategies/no-mr.js.map +1 -0
  178. package/dist/watch/strategies/pipeline-failed.d.ts +5 -0
  179. package/dist/watch/strategies/pipeline-failed.js +21 -0
  180. package/dist/watch/strategies/pipeline-failed.js.map +1 -0
  181. package/dist/watch/strategies/pipeline-running.d.ts +5 -0
  182. package/dist/watch/strategies/pipeline-running.js +21 -0
  183. package/dist/watch/strategies/pipeline-running.js.map +1 -0
  184. package/dist/watch/strategies/ready-for-release.d.ts +7 -0
  185. package/dist/watch/strategies/ready-for-release.js +35 -0
  186. package/dist/watch/strategies/ready-for-release.js.map +1 -0
  187. package/dist/watch/strategies/ready-to-commit.d.ts +5 -0
  188. package/dist/watch/strategies/ready-to-commit.js +21 -0
  189. package/dist/watch/strategies/ready-to-commit.js.map +1 -0
  190. package/dist/watch/strategies/ready-to-push.d.ts +5 -0
  191. package/dist/watch/strategies/ready-to-push.js +21 -0
  192. package/dist/watch/strategies/ready-to-push.js.map +1 -0
  193. package/dist/watch/strategies/released.d.ts +6 -0
  194. package/dist/watch/strategies/released.js +21 -0
  195. package/dist/watch/strategies/released.js.map +1 -0
  196. package/dist/watch/strategies/squash-merged.d.ts +6 -0
  197. package/dist/watch/strategies/squash-merged.js +21 -0
  198. package/dist/watch/strategies/squash-merged.js.map +1 -0
  199. package/dist/watch/strategies/types.d.ts +31 -0
  200. package/dist/watch/strategies/types.js +12 -0
  201. package/dist/watch/strategies/types.js.map +1 -0
  202. package/dist/watch/strategies/unstaged-changes.d.ts +5 -0
  203. package/dist/watch/strategies/unstaged-changes.js +21 -0
  204. package/dist/watch/strategies/unstaged-changes.js.map +1 -0
  205. package/dist/watch/strategies/wrong-branch.d.ts +6 -0
  206. package/dist/watch/strategies/wrong-branch.js +26 -0
  207. package/dist/watch/strategies/wrong-branch.js.map +1 -0
  208. package/dist/watch/types.d.ts +211 -0
  209. package/dist/watch/types.js +25 -0
  210. package/dist/watch/types.js.map +1 -0
  211. package/dist/watch/watcher-fs.d.ts +39 -0
  212. package/dist/watch/watcher-fs.js +105 -0
  213. package/dist/watch/watcher-fs.js.map +1 -0
  214. package/dist/watch/watcher-http.d.ts +19 -0
  215. package/dist/watch/watcher-http.js +47 -0
  216. package/dist/watch/watcher-http.js.map +1 -0
  217. package/dist/watch/watcher.d.ts +4 -0
  218. package/dist/watch/watcher.js +185 -0
  219. package/dist/watch/watcher.js.map +1 -0
  220. package/package.json +8 -2
  221. package/src/templates/about-watch.md +29 -0
  222. package/src/templates/hub.css +677 -0
  223. package/src/templates/hub.js +743 -0
  224. package/src/templates/qna.md +11 -0
  225. package/src/templates/steer.txt +2 -0
  226. package/src/templates/styles.css +53 -0
@@ -0,0 +1,11 @@
1
+ ---
2
+ id: {{id}}
3
+ status: asked
4
+ dateAsked: {{dateAsked}}
5
+ ---
6
+
7
+ ### Question
8
+ {{question}}
9
+
10
+ ### Answer
11
+
@@ -6,6 +6,7 @@ Do NOT run `logbook new` or `logbook release` — these are prompter responsibil
6
6
  Phase 1: Understand
7
7
  1. Read `ticket.md` in the active entry. This is your source of truth for requirements.
8
8
  2. Review `AGENTS.md` and `CONTRIBUTING.md` if you need architectural or workflow context.
9
+ 3. Check existing QnAs with `logbook answers` or check open questions with `logbook unanswered`.
9
10
 
10
11
  Phase 2: Execute & Trace
11
12
 
@@ -29,6 +30,7 @@ You MUST log your work continuously using `logbook log "<message>"`. This is not
29
30
  3. **One message per command call.** For multiple entries, call the command multiple times.
30
31
  4. Do NOT reconstruct the log at the end. If the log is empty when you finish, you did it wrong.
31
32
  5. Stick to the active entry; never modify past entries in the logbook folder.
33
+ 6. For short technical questions and findings, record them via `logbook ask "<question>"` and resolve them with `logbook answer <id> "<answer>"`.
32
34
 
33
35
  Phase 3: Synthesize
34
36
  1. When implementation is finished, write the narrative in `index.md`.
@@ -578,6 +578,16 @@ footer .build-time a:hover {
578
578
  color: #4b5563;
579
579
  }
580
580
 
581
+ .tag-asked {
582
+ background: #fef3c7;
583
+ color: #92400e;
584
+ }
585
+
586
+ .tag-answered {
587
+ background: #dcfce7;
588
+ color: #166534;
589
+ }
590
+
581
591
  /* Timeline card tag styling */
582
592
  .timeline-card .tags-wrapper {
583
593
  margin-top: 0.5rem;
@@ -1017,3 +1027,46 @@ article img[src$='.svg'] {
1017
1027
  .timeline-card--adr .tags-wrapper {
1018
1028
  margin-top: 0.5rem;
1019
1029
  }
1030
+
1031
+ /* QnA card styles */
1032
+ .qna-card {
1033
+ display: block;
1034
+ background: var(--card-bg, #ffffff);
1035
+ border: 1px solid var(--border, #e5e7eb);
1036
+ border-radius: 8px;
1037
+ padding: 1.25rem 1.5rem;
1038
+ margin-bottom: 1.5rem;
1039
+ }
1040
+
1041
+ .qna-card.unanswered {
1042
+ border-left: 4px solid #f59e0b;
1043
+ }
1044
+
1045
+ .qna-card.answered {
1046
+ border-left: 4px solid #10b981;
1047
+ }
1048
+
1049
+ .qna-meta {
1050
+ font-size: 0.8125rem;
1051
+ color: var(--text-muted);
1052
+ margin-bottom: 0.75rem;
1053
+ display: flex;
1054
+ align-items: center;
1055
+ gap: 0.5rem;
1056
+ flex-wrap: wrap;
1057
+ }
1058
+
1059
+ .qna-card h3 {
1060
+ font-size: 0.95rem;
1061
+ font-weight: 700;
1062
+ margin: 0.75rem 0 0.25rem 0;
1063
+ text-transform: uppercase;
1064
+ letter-spacing: 0.05em;
1065
+ color: var(--text-muted);
1066
+ }
1067
+
1068
+ .qna-card .markdown-body {
1069
+ color: var(--text);
1070
+ font-size: 0.9375rem;
1071
+ line-height: 1.6;
1072
+ }
@@ -1,4 +1,4 @@
1
- /** Parse a ticket ID string like "LB-123" or "LB-123-slug" into prefix and number. */
1
+ /** Parse a ticket ID string like "LB-123", "#45", or "#45-slug" into prefix and number. */
2
2
  export declare function parseTicketId(input: string): {
3
3
  prefix: string;
4
4
  number: number;
package/dist/utils/id.js CHANGED
@@ -1,7 +1,10 @@
1
1
  import fs from 'fs-extra';
2
2
  import { join } from 'node:path';
3
- /** Parse a ticket ID string like "LB-123" or "LB-123-slug" into prefix and number. */
3
+ /** Parse a ticket ID string like "LB-123", "#45", or "#45-slug" into prefix and number. */
4
4
  export function parseTicketId(input) {
5
+ if (/^#\d+(?:-.*)?$/.test(input)) {
6
+ return { prefix: '#', number: parseInt(input.replace(/^#/, ''), 10) };
7
+ }
5
8
  const match = input.match(/^([A-Za-z]+)-(\d+)(?:-.*)?$/);
6
9
  if (match) {
7
10
  return { prefix: match[1].toUpperCase(), number: parseInt(match[2], 10) };
@@ -1 +1 @@
1
- {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,sFAAsF;AACtF,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,MAAc;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC;IAEnE,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;gBACpB,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,CAAC;IACjC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,UAAU,EAAE,CAAC;AACjD,CAAC"}
1
+ {"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACxE,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,MAAc;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC;IAEnE,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;gBACpB,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,CAAC;IACjC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,UAAU,EAAE,CAAC;AACjD,CAAC"}
@@ -0,0 +1,46 @@
1
+ export type QnaStatus = 'asked' | 'answered' | 'deprecated';
2
+ export interface QnaItem {
3
+ id: number;
4
+ status: QnaStatus;
5
+ dateAsked: string;
6
+ dateAnswered?: string;
7
+ dateDeprecated?: string;
8
+ question: string;
9
+ answer: string;
10
+ questionHtml?: string;
11
+ answerHtml?: string;
12
+ filename: string;
13
+ filepath: string;
14
+ }
15
+ export declare function resolveQnaEntry(qnaDir: string, idInput: string): Promise<{
16
+ id: number;
17
+ item: QnaItem;
18
+ filepath: string;
19
+ } | {
20
+ error: string;
21
+ }>;
22
+ export declare function sortQnaItems<T extends {
23
+ status: QnaStatus;
24
+ dateAsked: string;
25
+ id: number;
26
+ }>(items: T[]): T[];
27
+ export declare function slugifyQuestion(text: string, maxLen?: number): string;
28
+ export declare function parseQnaContent(body: string): {
29
+ question: string;
30
+ answer: string;
31
+ };
32
+ export declare function formatQnaMarkdown(params: {
33
+ id: number;
34
+ status: QnaStatus;
35
+ dateAsked: string;
36
+ dateAnswered?: string;
37
+ dateDeprecated?: string;
38
+ question: string;
39
+ answer?: string;
40
+ }): string;
41
+ export declare function getAllQna(qnaDir: string): Promise<QnaItem[]>;
42
+ export declare function getNextQnaId(qnaDir: string): Promise<number>;
43
+ export declare function findQnaById(qnaDir: string, id: number): Promise<{
44
+ item: QnaItem;
45
+ filepath: string;
46
+ } | null>;
@@ -0,0 +1,157 @@
1
+ import fs from 'fs-extra';
2
+ import { join } from 'node:path';
3
+ import matter from 'gray-matter';
4
+ function isQnaStatus(value) {
5
+ return value === 'asked' || value === 'answered' || value === 'deprecated';
6
+ }
7
+ function parseNumericId(idInput) {
8
+ const id = parseInt(idInput, 10);
9
+ return isNaN(id) ? null : id;
10
+ }
11
+ export async function resolveQnaEntry(qnaDir, idInput) {
12
+ const id = parseNumericId(idInput);
13
+ if (id === null) {
14
+ return { error: `Invalid ID '${idInput}': must be a numeric value.` };
15
+ }
16
+ const found = await findQnaById(qnaDir, id);
17
+ if (!found) {
18
+ return { error: `QnA question #${id} not found.` };
19
+ }
20
+ return { id, item: found.item, filepath: found.filepath };
21
+ }
22
+ export function sortQnaItems(items) {
23
+ return [...items].sort((a, b) => {
24
+ if (a.status === 'asked' && b.status !== 'asked')
25
+ return -1;
26
+ if (a.status !== 'asked' && b.status === 'asked')
27
+ return 1;
28
+ const timeA = new Date(a.dateAsked).getTime() || 0;
29
+ const timeB = new Date(b.dateAsked).getTime() || 0;
30
+ if (timeA !== timeB)
31
+ return timeA - timeB;
32
+ return a.id - b.id;
33
+ });
34
+ }
35
+ export function slugifyQuestion(text, maxLen = 40) {
36
+ let slug = text
37
+ .toLowerCase()
38
+ .replace(/[^a-z0-9]+/g, '-')
39
+ .replace(/^-+|-+$/g, '');
40
+ if (slug.length > maxLen) {
41
+ slug = slug.substring(0, maxLen).replace(/-+$/, '');
42
+ }
43
+ return slug || 'question';
44
+ }
45
+ export function parseQnaContent(body) {
46
+ const questionMatch = body.match(/###\s+Question\s*\n([\s\S]*?)(?=###\s+Answer|$)/i);
47
+ const answerMatch = body.match(/###\s+Answer\s*\n([\s\S]*)$/i);
48
+ return {
49
+ question: questionMatch ? questionMatch[1].trim() : '',
50
+ answer: answerMatch ? answerMatch[1].trim() : '',
51
+ };
52
+ }
53
+ export function formatQnaMarkdown(params) {
54
+ const lines = ['---', `id: ${params.id}`, `status: ${params.status}`, `dateAsked: ${params.dateAsked}`];
55
+ if (params.dateAnswered)
56
+ lines.push(`dateAnswered: ${params.dateAnswered}`);
57
+ if (params.dateDeprecated)
58
+ lines.push(`dateDeprecated: ${params.dateDeprecated}`);
59
+ lines.push('---', '', '### Question', params.question.trim(), '', '### Answer', (params.answer || '').trim(), '');
60
+ return lines.join('\n');
61
+ }
62
+ async function parseQnaFile(filepath) {
63
+ if (!(await fs.pathExists(filepath)))
64
+ return null;
65
+ const raw = await fs.readFile(filepath, 'utf8');
66
+ const { data, content } = matter(raw);
67
+ if (data.id === undefined)
68
+ return null;
69
+ const id = Number(data.id);
70
+ const status = isQnaStatus(data.status) ? data.status : 'asked';
71
+ let dateAsked = '';
72
+ if (data.dateAsked instanceof Date) {
73
+ dateAsked = data.dateAsked.toISOString().split('T')[0];
74
+ }
75
+ else if (typeof data.dateAsked === 'string') {
76
+ dateAsked = data.dateAsked.split('T')[0];
77
+ }
78
+ let dateAnswered;
79
+ if (data.dateAnswered instanceof Date) {
80
+ dateAnswered = data.dateAnswered.toISOString().split('T')[0];
81
+ }
82
+ else if (typeof data.dateAnswered === 'string') {
83
+ dateAnswered = data.dateAnswered.split('T')[0];
84
+ }
85
+ let dateDeprecated;
86
+ if (data.dateDeprecated instanceof Date) {
87
+ dateDeprecated = data.dateDeprecated.toISOString().split('T')[0];
88
+ }
89
+ else if (typeof data.dateDeprecated === 'string') {
90
+ dateDeprecated = data.dateDeprecated.split('T')[0];
91
+ }
92
+ const { question, answer } = parseQnaContent(content);
93
+ const filename = filepath.split('/').pop() || '';
94
+ return {
95
+ id,
96
+ status,
97
+ dateAsked,
98
+ dateAnswered,
99
+ dateDeprecated,
100
+ question,
101
+ answer,
102
+ filename,
103
+ filepath,
104
+ };
105
+ }
106
+ export async function getAllQna(qnaDir) {
107
+ if (!(await fs.pathExists(qnaDir)))
108
+ return [];
109
+ const files = await fs.readdir(qnaDir);
110
+ const items = [];
111
+ for (const file of files) {
112
+ if (!file.endsWith('.md'))
113
+ continue;
114
+ const filepath = join(qnaDir, file);
115
+ if ((await fs.stat(filepath)).isDirectory())
116
+ continue;
117
+ const item = await parseQnaFile(filepath);
118
+ if (item)
119
+ items.push(item);
120
+ }
121
+ return items;
122
+ }
123
+ export async function getNextQnaId(qnaDir) {
124
+ if (!(await fs.pathExists(qnaDir)))
125
+ return 1;
126
+ const files = await fs.readdir(qnaDir);
127
+ let maxId = 0;
128
+ for (const file of files) {
129
+ if (!file.endsWith('.md'))
130
+ continue;
131
+ const m = file.match(/^(\d+)-/);
132
+ if (m) {
133
+ const num = parseInt(m[1], 10);
134
+ if (num > maxId)
135
+ maxId = num;
136
+ }
137
+ // Also try frontmatter if needed
138
+ const filepath = join(qnaDir, file);
139
+ try {
140
+ const item = await parseQnaFile(filepath);
141
+ if (item && item.id > maxId)
142
+ maxId = item.id;
143
+ }
144
+ catch {
145
+ // ignore
146
+ }
147
+ }
148
+ return maxId + 1;
149
+ }
150
+ export async function findQnaById(qnaDir, id) {
151
+ const items = await getAllQna(qnaDir);
152
+ const match = items.find((q) => q.id === id);
153
+ if (!match)
154
+ return null;
155
+ return { item: match, filepath: match.filepath };
156
+ }
157
+ //# sourceMappingURL=qna.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qna.js","sourceRoot":"","sources":["../../src/utils/qna.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { extractTicketFromBranch } from '../git-utils.js';
3
+ describe('extractTicketFromBranch', () => {
4
+ it('extracts a Jira ticket by configured prefix', () => {
5
+ expect(extractTicketFromBranch('feat/LB-34-cool-thing', 'LB', undefined)).toBe('LB-34');
6
+ });
7
+ it('extracts a bare numeric GitLab work item when a gitlab prefix is set', () => {
8
+ expect(extractTicketFromBranch('feat/36-content-api-garbage-endpoints', 'LB', '#')).toBe('#36');
9
+ });
10
+ it('handles a literal #-prefixed GitLab branch segment', () => {
11
+ expect(extractTicketFromBranch('feat/#32-api-anbindung', 'LB', '#')).toBe('#32');
12
+ });
13
+ it('returns undefined when neither prefix matches', () => {
14
+ expect(extractTicketFromBranch('feat/36-thing', 'LB', undefined)).toBeUndefined();
15
+ });
16
+ });
17
+ //# sourceMappingURL=git-utils.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-utils.test.js","sourceRoot":"","sources":["../../../src/watch/__tests__/git-utils.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,uBAAuB,CAAC,uBAAuB,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,uBAAuB,CAAC,uCAAuC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,uBAAuB,CAAC,wBAAwB,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,uBAAuB,CAAC,eAAe,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Detail metrics collector for logbook watch.
3
+ *
4
+ * Provides the expanded detail view data (LOC, staged/unstaged, commits).
5
+ * Uses shared git-utils helpers to avoid duplication with the git collector.
6
+ */
7
+ import type { ProjectDetails } from './types.js';
8
+ /**
9
+ * Collect detailed project metrics for the expandable detail view.
10
+ *
11
+ * @param cwd - Project root directory containing .project-logbook config.
12
+ * @returns The detailed metrics snapshot.
13
+ */
14
+ export declare function collectDetails(cwd: string): Promise<ProjectDetails>;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Detail metrics collector for logbook watch.
3
+ *
4
+ * Provides the expanded detail view data (LOC, staged/unstaged, commits).
5
+ * Uses shared git-utils helpers to avoid duplication with the git collector.
6
+ */
7
+ import { getConfig, getLogbookDirPath } from '../lib/config.js';
8
+ import { getDefaultBranch, getChangedLOC } from '../lib/git-helpers.js';
9
+ import { parseGitStatus, commitsAheadOfRemote, commitsVsDefault } from './git-utils.js';
10
+ /**
11
+ * Collect detailed project metrics for the expandable detail view.
12
+ *
13
+ * @param cwd - Project root directory containing .project-logbook config.
14
+ * @returns The detailed metrics snapshot.
15
+ */
16
+ export async function collectDetails(cwd) {
17
+ const config = getConfig(cwd);
18
+ const entriesDir = getLogbookDirPath(config, cwd);
19
+ const defaultBranch = getDefaultBranch(cwd);
20
+ const loc = await getChangedLOC(entriesDir, cwd);
21
+ const { staged, unstaged } = parseGitStatus(cwd);
22
+ return {
23
+ locChanged: loc.total,
24
+ insertions: loc.insertions,
25
+ deletions: loc.deletions,
26
+ unstagedFiles: unstaged,
27
+ stagedFiles: staged,
28
+ commitsAhead: commitsAheadOfRemote(cwd),
29
+ commitsOnBranch: commitsVsDefault(cwd, defaultBranch),
30
+ defaultBranch,
31
+ };
32
+ }
33
+ //# sourceMappingURL=collector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/watch/collector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGxF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAEjD,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,KAAK;QACrB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,aAAa,EAAE,QAAQ;QACvB,WAAW,EAAE,MAAM;QACnB,YAAY,EAAE,oBAAoB,CAAC,GAAG,CAAC;QACvC,eAAe,EAAE,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC;QACrD,aAAa;KACd,CAAC;AACJ,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * acli collector for logbook watch.
3
+ *
4
+ * Uses the Atlassian CLI (`acli`) tool to fetch Jira issues assigned to the
5
+ * current user. Runs on interval (60s) since it calls an external API.
6
+ *
7
+ * Auth sources (checked in order):
8
+ * 1. Shell env vars: JIRA_SITE, JIRA_EMAIL, JIRA_KEY
9
+ * 2. .env file in project root
10
+ * 3. Already-authenticated acli session
11
+ *
12
+ * Falls back gracefully when acli is not installed or auth is missing.
13
+ */
14
+ import type { Collector } from './types.js';
15
+ export declare const acliCollector: Collector;
@@ -0,0 +1,197 @@
1
+ /**
2
+ * acli collector for logbook watch.
3
+ *
4
+ * Uses the Atlassian CLI (`acli`) tool to fetch Jira issues assigned to the
5
+ * current user. Runs on interval (60s) since it calls an external API.
6
+ *
7
+ * Auth sources (checked in order):
8
+ * 1. Shell env vars: JIRA_SITE, JIRA_EMAIL, JIRA_KEY
9
+ * 2. .env file in project root
10
+ * 3. Already-authenticated acli session
11
+ *
12
+ * Falls back gracefully when acli is not installed or auth is missing.
13
+ */
14
+ import { execSync, exec } from 'node:child_process';
15
+ import { existsSync, readFileSync } from 'node:fs';
16
+ import { promisify } from 'node:util';
17
+ import { join } from 'node:path';
18
+ import { getConfig, GITLAB_PREFIX } from '../../lib/config.js';
19
+ import { getCurrentBranch } from '../../lib/git-helpers.js';
20
+ import { extractTicketFromBranch } from '../git-utils.js';
21
+ /** Check if acli CLI is installed. */
22
+ function isAcliInstalled() {
23
+ try {
24
+ execSync('acli --version', {
25
+ stdio: ['ignore', 'pipe', 'ignore'],
26
+ });
27
+ return true;
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /** Check if acli is already authenticated. */
34
+ function isAcliAuthenticated() {
35
+ try {
36
+ execSync('acli jira auth status', {
37
+ stdio: ['ignore', 'pipe', 'ignore'],
38
+ });
39
+ return true;
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
45
+ /** Parse .env file content, return matching vars. */
46
+ function parseEnvFile(cwd) {
47
+ const envPath = join(cwd, '.env');
48
+ if (!existsSync(envPath))
49
+ return {};
50
+ try {
51
+ const raw = readFileSync(envPath, 'utf8');
52
+ const result = {};
53
+ for (const line of raw.split('\n')) {
54
+ const trimmed = line.trim();
55
+ if (!trimmed || trimmed.startsWith('#'))
56
+ continue;
57
+ const eq = trimmed.indexOf('=');
58
+ if (eq === -1)
59
+ continue;
60
+ const key = trimmed.slice(0, eq).trim();
61
+ const val = trimmed
62
+ .slice(eq + 1)
63
+ .trim()
64
+ .replace(/^["']|["']$/g, '');
65
+ if (key === 'JIRA_SITE')
66
+ result.site = val;
67
+ else if (key === 'JIRA_EMAIL')
68
+ result.email = val;
69
+ else if (key === 'JIRA_KEY')
70
+ result.key = val;
71
+ }
72
+ return result;
73
+ }
74
+ catch {
75
+ return {};
76
+ }
77
+ }
78
+ /** Resolve auth credentials from env vars, falling back to .env. */
79
+ function resolveAuth(cwd) {
80
+ const site = process.env.JIRA_SITE;
81
+ const email = process.env.JIRA_EMAIL;
82
+ const key = process.env.JIRA_KEY;
83
+ if (site && email && key) {
84
+ return { site, email, key };
85
+ }
86
+ const envFile = parseEnvFile(cwd);
87
+ if (envFile.site && envFile.email && envFile.key) {
88
+ return { site: envFile.site, email: envFile.email, key: envFile.key };
89
+ }
90
+ return null;
91
+ }
92
+ function loginWithEnv(auth) {
93
+ try {
94
+ execSync(`echo "${auth.key}" | acli jira auth login --site "${auth.site}" --email "${auth.email}" --token`, {
95
+ stdio: ['ignore', 'pipe', 'ignore'],
96
+ shell: '/bin/bash',
97
+ timeout: 15_000,
98
+ });
99
+ return true;
100
+ }
101
+ catch {
102
+ return false;
103
+ }
104
+ }
105
+ /** Fetch real `updated` timestamps from Jira in parallel. */
106
+ async function fetchUpdatedTimestamps(keys) {
107
+ const results = new Map();
108
+ const run = promisify(exec);
109
+ const cmds = keys.map(async (key) => {
110
+ try {
111
+ const { stdout } = await run(`acli jira workitem view ${key} --fields 'updated' --json`, { timeout: 10_000 });
112
+ const data = JSON.parse(stdout);
113
+ if (data?.fields?.updated)
114
+ results.set(key, data.fields.updated);
115
+ }
116
+ catch {
117
+ /* skip */
118
+ }
119
+ });
120
+ await Promise.all(cmds);
121
+ return results;
122
+ }
123
+ export const acliCollector = {
124
+ name: 'acli',
125
+ schedule: { type: 'interval', ms: 60_000 },
126
+ async available(cwd) {
127
+ return isAcliInstalled() && (!!resolveAuth(cwd) || isAcliAuthenticated());
128
+ },
129
+ async collect(cwd, state) {
130
+ try {
131
+ const auth = resolveAuth(cwd);
132
+ if (auth && !isAcliAuthenticated())
133
+ loginWithEnv(auth);
134
+ if (!isAcliAuthenticated()) {
135
+ state.jiraAssignedIssues = [];
136
+ state.jiraStatus = undefined;
137
+ state.jiraLastUpdated = new Date().toISOString();
138
+ return;
139
+ }
140
+ let site = auth?.site ?? '';
141
+ if (!site) {
142
+ try {
143
+ const out = execSync('acli jira auth status --json 2>/dev/null || acli jira auth status', {
144
+ encoding: 'utf8',
145
+ stdio: ['ignore', 'pipe', 'pipe'],
146
+ timeout: 5_000,
147
+ });
148
+ const m = out.match(/Site:\s*(\S+)/);
149
+ if (m)
150
+ site = m[1];
151
+ }
152
+ catch { }
153
+ }
154
+ const baseUrl = site ? `https://${site}` : undefined;
155
+ const rawIssues = execSync(`acli jira workitem search --jql "assignee = currentUser() AND resolution = Unresolved" --json`, { encoding: 'utf8', cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 15_000 }).trim();
156
+ if (!rawIssues) {
157
+ state.jiraAssignedIssues = [];
158
+ state.jiraLastUpdated = new Date().toISOString();
159
+ return;
160
+ }
161
+ const issues = JSON.parse(rawIssues);
162
+ const updatedMap = await fetchUpdatedTimestamps(issues.map((i) => i.key));
163
+ state.jiraAssignedIssues = issues.map((issue) => ({
164
+ key: issue.key,
165
+ summary: issue.fields.summary,
166
+ status: issue.fields.status.name,
167
+ url: baseUrl ? `${baseUrl}/browse/${issue.key}` : '',
168
+ jiraBaseUrl: baseUrl ?? '',
169
+ updated: updatedMap.get(issue.key) ?? new Date().toISOString(),
170
+ }));
171
+ const config = getConfig(cwd);
172
+ const ticketKey = state.activeTicketId ?? extractTicketFromBranch(getCurrentBranch(cwd), config.jiraPrefix, GITLAB_PREFIX);
173
+ if (ticketKey) {
174
+ try {
175
+ const issueOut = execSync(`acli jira workitem view ${ticketKey} --json`, {
176
+ encoding: 'utf8',
177
+ cwd,
178
+ stdio: ['ignore', 'pipe', 'ignore'],
179
+ timeout: 10_000,
180
+ }).trim();
181
+ if (issueOut) {
182
+ const issue = JSON.parse(issueOut);
183
+ state.jiraStatus = issue.fields.status.name;
184
+ }
185
+ else
186
+ state.jiraStatus = undefined;
187
+ }
188
+ catch {
189
+ state.jiraStatus = undefined;
190
+ }
191
+ }
192
+ state.jiraLastUpdated = new Date().toISOString();
193
+ }
194
+ catch { }
195
+ },
196
+ };
197
+ //# sourceMappingURL=acli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"acli.js","sourceRoot":"","sources":["../../../src/watch/collectors/acli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Logbook entries collector for logbook watch.
3
+ *
4
+ * Reads the active lockfile, checks for narrative placeholders, and detects
5
+ * released entries. Runs on file-system events (instant feedback when the
6
+ * lockfile or entry files change).
7
+ */
8
+ import type { Collector } from './types.js';
9
+ export declare const entriesCollector: Collector;