project-logbook 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +2 -0
  2. package/dist/commands/build.js +7 -4
  3. package/dist/commands/build.js.map +1 -1
  4. package/dist/commands/log.js +2 -0
  5. package/dist/commands/log.js.map +1 -1
  6. package/dist/commands/new-helpers.d.ts +0 -1
  7. package/dist/commands/new-helpers.js +6 -3
  8. package/dist/commands/new-helpers.js.map +1 -1
  9. package/dist/commands/release.js +2 -0
  10. package/dist/commands/release.js.map +1 -1
  11. package/dist/commands/start.js +90 -21
  12. package/dist/commands/start.js.map +1 -1
  13. package/dist/commands/status.js +3 -0
  14. package/dist/commands/status.js.map +1 -1
  15. package/dist/commands/watch.d.ts +15 -0
  16. package/dist/commands/watch.js +153 -0
  17. package/dist/commands/watch.js.map +1 -0
  18. package/dist/index.js +7 -0
  19. package/dist/index.js.map +1 -1
  20. package/dist/lib/autolink.d.ts +5 -0
  21. package/dist/lib/autolink.js +36 -2
  22. package/dist/lib/autolink.js.map +1 -1
  23. package/dist/lib/build-helpers.d.ts +10 -1
  24. package/dist/lib/build-helpers.js +27 -4
  25. package/dist/lib/build-helpers.js.map +1 -1
  26. package/dist/lib/build-steps.js +2 -1
  27. package/dist/lib/build-steps.js.map +1 -1
  28. package/dist/lib/config.d.ts +8 -0
  29. package/dist/lib/config.js +7 -0
  30. package/dist/lib/config.js.map +1 -1
  31. package/dist/lib/custom-card-renderer.js +2 -1
  32. package/dist/lib/custom-card-renderer.js.map +1 -1
  33. package/dist/lib/entry-href.d.ts +7 -0
  34. package/dist/lib/entry-href.js +8 -0
  35. package/dist/lib/entry-href.js.map +1 -0
  36. package/dist/lib/markdown-processors.d.ts +2 -1
  37. package/dist/lib/post-template.js +3 -3
  38. package/dist/lib/post-template.js.map +1 -1
  39. package/dist/lib/template-helpers.js +3 -2
  40. package/dist/lib/template-helpers.js.map +1 -1
  41. package/dist/lib/template-types.d.ts +2 -0
  42. package/dist/lib/templates.js +5 -3
  43. package/dist/lib/templates.js.map +1 -1
  44. package/dist/linters/jira-prefix.js +32 -16
  45. package/dist/linters/jira-prefix.js.map +1 -1
  46. package/dist/templates/about-watch.md +29 -0
  47. package/dist/templates/hub.css +677 -0
  48. package/dist/templates/hub.js +628 -0
  49. package/dist/utils/id.d.ts +1 -1
  50. package/dist/utils/id.js +4 -1
  51. package/dist/utils/id.js.map +1 -1
  52. package/dist/watch/collector.d.ts +14 -0
  53. package/dist/watch/collector.js +33 -0
  54. package/dist/watch/collector.js.map +1 -0
  55. package/dist/watch/collectors/acli.d.ts +15 -0
  56. package/dist/watch/collectors/acli.js +197 -0
  57. package/dist/watch/collectors/acli.js.map +1 -0
  58. package/dist/watch/collectors/entries.d.ts +9 -0
  59. package/dist/watch/collectors/entries.js +90 -0
  60. package/dist/watch/collectors/entries.js.map +1 -0
  61. package/dist/watch/collectors/git.d.ts +12 -0
  62. package/dist/watch/collectors/git.js +58 -0
  63. package/dist/watch/collectors/git.js.map +1 -0
  64. package/dist/watch/collectors/glab.d.ts +10 -0
  65. package/dist/watch/collectors/glab.js +184 -0
  66. package/dist/watch/collectors/glab.js.map +1 -0
  67. package/dist/watch/collectors/index.d.ts +10 -0
  68. package/dist/watch/collectors/index.js +21 -0
  69. package/dist/watch/collectors/index.js.map +1 -0
  70. package/dist/watch/collectors/jira.d.ts +3 -0
  71. package/dist/watch/collectors/jira.js +110 -0
  72. package/dist/watch/collectors/jira.js.map +1 -0
  73. package/dist/watch/collectors/type-guards.d.ts +17 -0
  74. package/dist/watch/collectors/type-guards.js +35 -0
  75. package/dist/watch/collectors/type-guards.js.map +1 -0
  76. package/dist/watch/collectors/types.d.ts +37 -0
  77. package/dist/watch/collectors/types.js +9 -0
  78. package/dist/watch/collectors/types.js.map +1 -0
  79. package/dist/watch/dashboard-html.d.ts +11 -0
  80. package/dist/watch/dashboard-html.js +73 -0
  81. package/dist/watch/dashboard-html.js.map +1 -0
  82. package/dist/watch/dashboard-script.d.ts +12 -0
  83. package/dist/watch/dashboard-script.js +20 -0
  84. package/dist/watch/dashboard-script.js.map +1 -0
  85. package/dist/watch/dashboard-styles.d.ts +12 -0
  86. package/dist/watch/dashboard-styles.js +20 -0
  87. package/dist/watch/dashboard-styles.js.map +1 -0
  88. package/dist/watch/dashboard.d.ts +12 -0
  89. package/dist/watch/dashboard.js +50 -0
  90. package/dist/watch/dashboard.js.map +1 -0
  91. package/dist/watch/git-utils.d.ts +30 -0
  92. package/dist/watch/git-utils.js +101 -0
  93. package/dist/watch/git-utils.js.map +1 -0
  94. package/dist/watch/hub-state.d.ts +37 -0
  95. package/dist/watch/hub-state.js +55 -0
  96. package/dist/watch/hub-state.js.map +1 -0
  97. package/dist/watch/hub.d.ts +2 -0
  98. package/dist/watch/hub.js +219 -0
  99. package/dist/watch/hub.js.map +1 -0
  100. package/dist/watch/log-utils.d.ts +13 -0
  101. package/dist/watch/log-utils.js +24 -0
  102. package/dist/watch/log-utils.js.map +1 -0
  103. package/dist/watch/notify.d.ts +18 -0
  104. package/dist/watch/notify.js +45 -0
  105. package/dist/watch/notify.js.map +1 -0
  106. package/dist/watch/state.d.ts +34 -0
  107. package/dist/watch/state.js +36 -0
  108. package/dist/watch/state.js.map +1 -0
  109. package/dist/watch/strategies/behind-default.d.ts +5 -0
  110. package/dist/watch/strategies/behind-default.js +21 -0
  111. package/dist/watch/strategies/behind-default.js.map +1 -0
  112. package/dist/watch/strategies/idle.d.ts +5 -0
  113. package/dist/watch/strategies/idle.js +21 -0
  114. package/dist/watch/strategies/idle.js.map +1 -0
  115. package/dist/watch/strategies/in-progress.d.ts +5 -0
  116. package/dist/watch/strategies/in-progress.js +21 -0
  117. package/dist/watch/strategies/in-progress.js.map +1 -0
  118. package/dist/watch/strategies/index.d.ts +34 -0
  119. package/dist/watch/strategies/index.js +148 -0
  120. package/dist/watch/strategies/index.js.map +1 -0
  121. package/dist/watch/strategies/jira-not-in-review.d.ts +2 -0
  122. package/dist/watch/strategies/jira-not-in-review.js +29 -0
  123. package/dist/watch/strategies/jira-not-in-review.js.map +1 -0
  124. package/dist/watch/strategies/jira-still-open.d.ts +2 -0
  125. package/dist/watch/strategies/jira-still-open.js +31 -0
  126. package/dist/watch/strategies/jira-still-open.js.map +1 -0
  127. package/dist/watch/strategies/mr-approval-optional.d.ts +5 -0
  128. package/dist/watch/strategies/mr-approval-optional.js +21 -0
  129. package/dist/watch/strategies/mr-approval-optional.js.map +1 -0
  130. package/dist/watch/strategies/mr-approved.d.ts +5 -0
  131. package/dist/watch/strategies/mr-approved.js +21 -0
  132. package/dist/watch/strategies/mr-approved.js.map +1 -0
  133. package/dist/watch/strategies/mr-conflicts.d.ts +5 -0
  134. package/dist/watch/strategies/mr-conflicts.js +21 -0
  135. package/dist/watch/strategies/mr-conflicts.js.map +1 -0
  136. package/dist/watch/strategies/mr-draft.d.ts +5 -0
  137. package/dist/watch/strategies/mr-draft.js +21 -0
  138. package/dist/watch/strategies/mr-draft.js.map +1 -0
  139. package/dist/watch/strategies/mr-open-comments.d.ts +5 -0
  140. package/dist/watch/strategies/mr-open-comments.js +21 -0
  141. package/dist/watch/strategies/mr-open-comments.js.map +1 -0
  142. package/dist/watch/strategies/narrative-missing.d.ts +5 -0
  143. package/dist/watch/strategies/narrative-missing.js +21 -0
  144. package/dist/watch/strategies/narrative-missing.js.map +1 -0
  145. package/dist/watch/strategies/no-mr.d.ts +5 -0
  146. package/dist/watch/strategies/no-mr.js +21 -0
  147. package/dist/watch/strategies/no-mr.js.map +1 -0
  148. package/dist/watch/strategies/pipeline-failed.d.ts +5 -0
  149. package/dist/watch/strategies/pipeline-failed.js +21 -0
  150. package/dist/watch/strategies/pipeline-failed.js.map +1 -0
  151. package/dist/watch/strategies/pipeline-running.d.ts +5 -0
  152. package/dist/watch/strategies/pipeline-running.js +21 -0
  153. package/dist/watch/strategies/pipeline-running.js.map +1 -0
  154. package/dist/watch/strategies/ready-for-release.d.ts +7 -0
  155. package/dist/watch/strategies/ready-for-release.js +35 -0
  156. package/dist/watch/strategies/ready-for-release.js.map +1 -0
  157. package/dist/watch/strategies/ready-to-commit.d.ts +5 -0
  158. package/dist/watch/strategies/ready-to-commit.js +21 -0
  159. package/dist/watch/strategies/ready-to-commit.js.map +1 -0
  160. package/dist/watch/strategies/ready-to-push.d.ts +5 -0
  161. package/dist/watch/strategies/ready-to-push.js +21 -0
  162. package/dist/watch/strategies/ready-to-push.js.map +1 -0
  163. package/dist/watch/strategies/released.d.ts +6 -0
  164. package/dist/watch/strategies/released.js +21 -0
  165. package/dist/watch/strategies/released.js.map +1 -0
  166. package/dist/watch/strategies/squash-merged.d.ts +6 -0
  167. package/dist/watch/strategies/squash-merged.js +21 -0
  168. package/dist/watch/strategies/squash-merged.js.map +1 -0
  169. package/dist/watch/strategies/types.d.ts +31 -0
  170. package/dist/watch/strategies/types.js +12 -0
  171. package/dist/watch/strategies/types.js.map +1 -0
  172. package/dist/watch/strategies/unstaged-changes.d.ts +5 -0
  173. package/dist/watch/strategies/unstaged-changes.js +21 -0
  174. package/dist/watch/strategies/unstaged-changes.js.map +1 -0
  175. package/dist/watch/strategies/wrong-branch.d.ts +6 -0
  176. package/dist/watch/strategies/wrong-branch.js +26 -0
  177. package/dist/watch/strategies/wrong-branch.js.map +1 -0
  178. package/dist/watch/types.d.ts +190 -0
  179. package/dist/watch/types.js +25 -0
  180. package/dist/watch/types.js.map +1 -0
  181. package/dist/watch/watcher-fs.d.ts +39 -0
  182. package/dist/watch/watcher-fs.js +105 -0
  183. package/dist/watch/watcher-fs.js.map +1 -0
  184. package/dist/watch/watcher-http.d.ts +19 -0
  185. package/dist/watch/watcher-http.js +47 -0
  186. package/dist/watch/watcher-http.js.map +1 -0
  187. package/dist/watch/watcher.d.ts +4 -0
  188. package/dist/watch/watcher.js +174 -0
  189. package/dist/watch/watcher.js.map +1 -0
  190. package/package.json +2 -1
  191. package/src/templates/about-watch.md +29 -0
  192. package/src/templates/hub.css +677 -0
  193. package/src/templates/hub.js +628 -0
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Collector registry for logbook watch.
3
+ *
4
+ * Exports all available collectors. The watcher runner checks `available()`
5
+ * for each and only activates those whose prerequisites are met.
6
+ */
7
+ import type { Collector } from './types.js';
8
+ /** All registered collectors. */
9
+ /** acli takes priority over jira when both are available — order matters. */
10
+ export declare const ALL_COLLECTORS: Collector[];
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Collector registry for logbook watch.
3
+ *
4
+ * Exports all available collectors. The watcher runner checks `available()`
5
+ * for each and only activates those whose prerequisites are met.
6
+ */
7
+ import { gitCollector } from './git.js';
8
+ import { entriesCollector } from './entries.js';
9
+ import { glabCollector } from './glab.js';
10
+ import { acliCollector } from './acli.js';
11
+ import { jiraCollector } from './jira.js';
12
+ /** All registered collectors. */
13
+ /** acli takes priority over jira when both are available — order matters. */
14
+ export const ALL_COLLECTORS = [
15
+ gitCollector,
16
+ entriesCollector,
17
+ glabCollector,
18
+ acliCollector,
19
+ jiraCollector,
20
+ ];
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/watch/collectors/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AACxC,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,iCAAiC;AACjC,6EAA6E;AAC7E,MAAM,CAAC,MAAM,cAAc,GAAgB;IACzC,YAAY;IACZ,gBAAgB;IAChB,aAAa;IACb,aAAa;IACb,aAAa;CACd,CAAC"}
@@ -0,0 +1,3 @@
1
+ /** HTTP Jira collector — fallback when acli is not available. */
2
+ import type { Collector } from './types.js';
3
+ export declare const jiraCollector: Collector;
@@ -0,0 +1,110 @@
1
+ /** HTTP Jira collector — fallback when acli is not available. */
2
+ import { request } from 'node:https';
3
+ import { execSync } from 'node:child_process';
4
+ import { getConfig } from '../../lib/config.js';
5
+ import { getCurrentBranch } from '../../lib/git-helpers.js';
6
+ import { extractTicketFromBranch } from '../git-utils.js';
7
+ import { isJiraIssue } from './type-guards.js';
8
+ function buildAssignedJql(accountId) {
9
+ return `assignee = "${accountId}" AND resolution = Unresolved ORDER BY updated DESC`;
10
+ }
11
+ function httpGet(url, token, timeout = 5000) {
12
+ return new Promise((resolve) => {
13
+ const req = request(url, { method: 'GET', headers: { Authorization: `Bearer ${token}`, Accept: 'application/json' }, timeout }, (res) => {
14
+ const chunks = [];
15
+ res.on('data', (chunk) => chunks.push(chunk));
16
+ res.on('end', () => {
17
+ if (res.statusCode !== 200) {
18
+ resolve(null);
19
+ return;
20
+ }
21
+ try {
22
+ const parsed = JSON.parse(Buffer.concat(chunks).toString('utf8'));
23
+ resolve(parsed);
24
+ }
25
+ catch {
26
+ resolve(null);
27
+ }
28
+ });
29
+ });
30
+ req.on('error', () => resolve(null));
31
+ req.on('timeout', () => {
32
+ req.destroy();
33
+ resolve(null);
34
+ });
35
+ req.end();
36
+ });
37
+ }
38
+ function fetchMyself(baseUrl, token) {
39
+ return httpGet(new URL('/rest/api/3/myself', baseUrl), token, 5000);
40
+ }
41
+ function searchIssues(baseUrl, token, jql) {
42
+ const url = new URL('/rest/api/3/search', baseUrl);
43
+ url.searchParams.set('jql', jql);
44
+ url.searchParams.set('fields', 'summary,status,updated,assignee');
45
+ url.searchParams.set('maxResults', '50');
46
+ return httpGet(url, token, 10_000).then((body) => {
47
+ const issues = (body?.issues ?? []).filter(isJiraIssue);
48
+ return issues;
49
+ });
50
+ }
51
+ function fetchIssue(baseUrl, token, issueKey) {
52
+ const url = new URL(`/rest/api/3/issue/${issueKey}`, baseUrl);
53
+ url.searchParams.set('fields', 'summary,status,updated,assignee');
54
+ return httpGet(url, token, 5000).then((body) => (body && isJiraIssue(body) ? body : null));
55
+ }
56
+ function getJiraToken() {
57
+ return process.env.JIRA_API_TOKEN;
58
+ }
59
+ export const jiraCollector = {
60
+ name: 'jira',
61
+ schedule: { type: 'interval', ms: 60_000 },
62
+ async available(_cwd) {
63
+ if (!getJiraToken())
64
+ return false;
65
+ try {
66
+ execSync('which acli', { stdio: ['ignore', 'pipe', 'ignore'] });
67
+ return false;
68
+ }
69
+ catch {
70
+ return true;
71
+ }
72
+ },
73
+ async collect(cwd, state) {
74
+ try {
75
+ const config = getConfig(cwd);
76
+ const baseUrl = config.jiraBaseUrl;
77
+ const token = getJiraToken();
78
+ if (!baseUrl || !token) {
79
+ state.jiraStatus = undefined;
80
+ state.jiraAssignedIssues = [];
81
+ state.jiraLastUpdated = new Date().toISOString();
82
+ return;
83
+ }
84
+ const myself = await fetchMyself(baseUrl, token);
85
+ if (!myself) {
86
+ state.jiraAssignedIssues = [];
87
+ state.jiraLastUpdated = new Date().toISOString();
88
+ return;
89
+ }
90
+ const rawIssues = await searchIssues(baseUrl, token, buildAssignedJql(myself.accountId));
91
+ state.jiraAssignedIssues = rawIssues.map((issue) => ({
92
+ key: issue.key,
93
+ summary: issue.fields.summary,
94
+ status: issue.fields.status.name,
95
+ url: `${baseUrl}/browse/${issue.key}`,
96
+ jiraBaseUrl: baseUrl,
97
+ updated: issue.fields.updated,
98
+ }));
99
+ const branch = getCurrentBranch(cwd);
100
+ const ticketKey = state.activeTicketId ?? (branch ? extractTicketFromBranch(branch, config.jiraPrefix) : undefined);
101
+ if (ticketKey) {
102
+ const issue = await fetchIssue(baseUrl, token, ticketKey);
103
+ state.jiraStatus = issue?.fields.status.name;
104
+ }
105
+ state.jiraLastUpdated = new Date().toISOString();
106
+ }
107
+ catch { }
108
+ },
109
+ };
110
+ //# sourceMappingURL=jira.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jira.js","sourceRoot":"","sources":["../../../src/watch/collectors/jira.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /** Type guards for GitLab and Jira API response types. */
2
+ interface JiraIssueRaw {
3
+ key: string;
4
+ fields: {
5
+ summary: string;
6
+ status: {
7
+ name: string;
8
+ };
9
+ updated: string;
10
+ assignee: {
11
+ displayName: string;
12
+ } | null;
13
+ };
14
+ }
15
+ export declare function isJiraIssue(value: unknown): value is JiraIssueRaw;
16
+ export declare function parsePipelineStatus(status: string): 'running' | 'passed' | 'failed' | 'pending' | 'canceled' | 'none';
17
+ export {};
@@ -0,0 +1,35 @@
1
+ /** Type guards for GitLab and Jira API response types. */
2
+ export function isJiraIssue(value) {
3
+ if (!value || typeof value !== 'object')
4
+ return false;
5
+ if (!('key' in value) || !('fields' in value))
6
+ return false;
7
+ if (typeof value.key !== 'string')
8
+ return false;
9
+ const fields = value.fields;
10
+ if (!fields || typeof fields !== 'object')
11
+ return false;
12
+ if (!('summary' in fields) || !('status' in fields))
13
+ return false;
14
+ if (typeof fields.summary !== 'string')
15
+ return false;
16
+ const status = fields.status;
17
+ if (!status || typeof status !== 'object')
18
+ return false;
19
+ return 'name' in status && typeof status.name === 'string';
20
+ }
21
+ export function parsePipelineStatus(status) {
22
+ const lower = status.toLowerCase();
23
+ if (lower === 'running')
24
+ return 'running';
25
+ if (lower === 'passed' || lower === 'success')
26
+ return 'passed';
27
+ if (lower === 'failed')
28
+ return 'failed';
29
+ if (lower === 'pending')
30
+ return 'pending';
31
+ if (lower === 'canceled')
32
+ return 'canceled';
33
+ return 'none';
34
+ }
35
+ //# sourceMappingURL=type-guards.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"type-guards.js","sourceRoot":"","sources":["../../../src/watch/collectors/type-guards.ts"],"names":[],"mappings":"AAAA,0DAA0D;AAY1D,MAAM,UAAU,WAAW,CAAC,KAAc;IACxC,IAAI,CAAC,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACtD,IAAI,CAAC,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5D,IAAI,OAAO,KAAK,CAAC,GAAG,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAChD,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC;IAC5B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,IAAI,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,QAAQ,IAAI,MAAM,CAAC;QAAE,OAAO,KAAK,CAAC;IAClE,IAAI,OAAO,MAAM,CAAC,OAAO,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACrD,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;IAC7B,IAAI,CAAC,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACxD,OAAO,MAAM,IAAI,MAAM,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;AAC7D,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAc;IAChD,MAAM,KAAK,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IACnC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,QAAQ,CAAC;IAC/D,IAAI,KAAK,KAAK,QAAQ;QAAE,OAAO,QAAQ,CAAC;IACxC,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC1C,IAAI,KAAK,KAAK,UAAU;QAAE,OAAO,UAAU,CAAC;IAC5C,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Collector interface for logbook watch.
3
+ *
4
+ * A collector gathers a subset of metrics from a specific source (git, files,
5
+ * CLI tools, APIs). Each collector runs independently on its own schedule and
6
+ * writes its results into the shared WatcherState.
7
+ */
8
+ import type { WatcherState } from '../state.js';
9
+ /** Schedule type for a collector. */
10
+ export type CollectorSchedule =
11
+ /** Runs on file-system events (instant, debounced). */
12
+ {
13
+ type: 'fs';
14
+ patterns: string[];
15
+ }
16
+ /** Runs on a fixed interval. */
17
+ | {
18
+ type: 'interval';
19
+ ms: number;
20
+ };
21
+ /** A collector that gathers metrics from a specific source. */
22
+ export interface Collector {
23
+ /** Human-readable name for logging. */
24
+ name: string;
25
+ /** When this collector should run. */
26
+ schedule: CollectorSchedule;
27
+ /**
28
+ * Check if this collector is available (e.g., CLI tool installed).
29
+ * Returns false if prerequisites are missing — collector will be skipped.
30
+ */
31
+ available(cwd: string): Promise<boolean>;
32
+ /**
33
+ * Collect metrics and write them into the shared state.
34
+ * Must not throw — failures should result in undefined/default values.
35
+ */
36
+ collect(cwd: string, state: WatcherState): Promise<void>;
37
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Collector interface for logbook watch.
3
+ *
4
+ * A collector gathers a subset of metrics from a specific source (git, files,
5
+ * CLI tools, APIs). Each collector runs independently on its own schedule and
6
+ * writes its results into the shared WatcherState.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/watch/collectors/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Dashboard HTML structure for logbook watch hub.
3
+ *
4
+ * Provides the page skeleton with tab navigation and content areas.
5
+ */
6
+ /**
7
+ * Generate the HTML body structure with tab navigation.
8
+ *
9
+ * @param aboutHtml - Pre-rendered HTML content for the About tab.
10
+ */
11
+ export declare function dashboardHtml(aboutHtml: string): string;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Dashboard HTML structure for logbook watch hub.
3
+ *
4
+ * Provides the page skeleton with tab navigation and content areas.
5
+ */
6
+ /**
7
+ * Generate the HTML body structure with tab navigation.
8
+ *
9
+ * @param aboutHtml - Pre-rendered HTML content for the About tab.
10
+ */
11
+ export function dashboardHtml(aboutHtml) {
12
+ return `
13
+ <header>
14
+ <h1>Logbook Hub</h1>
15
+ </header>
16
+ <nav class="tab-bar">
17
+ <button class="tab-btn active" data-tab="projects">Local Projects <span class="tab-count" id="project-count">0</span></button>
18
+ <button class="tab-btn" data-tab="jira">Jira <span class="tab-count" id="jira-count">0</span></button>
19
+ <button class="tab-btn" data-tab="gitlab">GitLab <span class="tab-count" id="gitlab-count">0</span></button>
20
+ <button class="tab-btn" data-tab="help">Help</button>
21
+ <button class="tab-btn" data-tab="about">About</button>
22
+ </nav>
23
+ <div class="container">
24
+ <div id="tab-projects" class="tab-content active">
25
+ <div class="tab-header-row" style="justify-content: flex-end;">
26
+ <button class="sort-toggle" id="sort-toggle">Sort (Newest)</button>
27
+ </div>
28
+ <div id="projects" class="projects">
29
+ <div class="empty">Waiting for projects to connect...</div>
30
+ </div>
31
+ </div>
32
+ <div id="tab-help" class="tab-content">
33
+ <h2 class="tab-heading">Status Badges Reference</h2>
34
+ <p class="tab-description">
35
+ These badges appear on project cards based on the current state of your code, merge requests, and Jira tickets.
36
+ Badges marked "blocked by" will not appear if the blocking badge is already active.
37
+ </p>
38
+ <table class="help-table">
39
+ <thead><tr><th>Badge</th><th>Source</th><th>Severity</th><th>Description</th><th>Blocked By</th></tr></thead>
40
+ <tbody id="help-table-body"><tr><td colspan="5" class="table-loading">Loading...</td></tr></tbody>
41
+ </table>
42
+ </div>
43
+ <div id="tab-about" class="tab-content">
44
+ <div class="about-content">${aboutHtml}</div>
45
+ </div>
46
+ <div id="tab-jira" class="tab-content">
47
+ <div class="tab-header-row">
48
+ <h2 class="tab-heading">Assigned Jira Issues</h2>
49
+ <button class="sort-toggle" id="jira-sort-toggle">Sort (Updated)</button>
50
+ </div>
51
+ <div id="jira-issues-content">
52
+ <div class="placeholder-msg">No assigned Jira issues found.<br>Set the <code>JIRA_API_TOKEN</code> environment variable and connect a project with <code>jiraBaseUrl</code> configured.</div>
53
+ </div>
54
+ <div class="tab-collector-footer" id="jira-tab-footer" style="display:none"></div>
55
+ </div>
56
+ <div id="tab-gitlab" class="tab-content">
57
+ <h2 class="tab-heading">Open Merge Requests</h2>
58
+ <div id="gitlab-mrs-content">
59
+ <div class="placeholder-msg">No open merge requests found.<br>Install the <code>glab</code> CLI and run <code>glab auth login</code> to connect.</div>
60
+ </div>
61
+ <div class="tab-collector-footer" id="gitlab-tab-footer" style="display:none"></div>
62
+ </div>
63
+ </div>
64
+ <div class="connection-status" id="conn-status">connected</div>
65
+ <div class="lightbox" id="lightbox">
66
+ <div class="lightbox-content">
67
+ <button class="lightbox-close" id="lightbox-close">&times;</button>
68
+ <div class="lightbox-title" id="lightbox-title"></div>
69
+ <div class="lightbox-body" id="lightbox-body"></div>
70
+ </div>
71
+ </div>`;
72
+ }
73
+ //# sourceMappingURL=dashboard-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-html.js","sourceRoot":"","sources":["../../src/watch/dashboard-html.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,SAAiB;IAC7C,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;mCAgC0B,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;SA2BnC,CAAC;AACV,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Dashboard client-side JavaScript for logbook watch hub.
3
+ *
4
+ * Reads from the static hub.js template file instead of embedding JavaScript
5
+ * in a TypeScript string literal.
6
+ */
7
+ /**
8
+ * Read and return the Hub JavaScript wrapped in a script tag.
9
+ *
10
+ * @returns A `<script>` element string with the full dashboard JS.
11
+ */
12
+ export declare function dashboardScript(): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Dashboard client-side JavaScript for logbook watch hub.
3
+ *
4
+ * Reads from the static hub.js template file instead of embedding JavaScript
5
+ * in a TypeScript string literal.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { getTemplatesDir } from '../lib/migrations.js';
10
+ /**
11
+ * Read and return the Hub JavaScript wrapped in a script tag.
12
+ *
13
+ * @returns A `<script>` element string with the full dashboard JS.
14
+ */
15
+ export function dashboardScript() {
16
+ const jsPath = join(getTemplatesDir(), 'hub.js');
17
+ const js = readFileSync(jsPath, 'utf8');
18
+ return `<script>\n${js}\n</script>`;
19
+ }
20
+ //# sourceMappingURL=dashboard-script.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-script.js","sourceRoot":"","sources":["../../src/watch/dashboard-script.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,eAAe,EAAE,EAAE,QAAQ,CAAC,CAAC;IACjD,MAAM,EAAE,GAAG,YAAY,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,OAAO,aAAa,EAAE,aAAa,CAAC;AACtC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Dashboard CSS styles for logbook watch hub.
3
+ *
4
+ * Reads from the static hub.css template file instead of embedding CSS
5
+ * in a TypeScript string literal.
6
+ */
7
+ /**
8
+ * Read and return the Hub CSS wrapped in a style tag.
9
+ *
10
+ * @returns A `<style>` element string with the full dashboard CSS.
11
+ */
12
+ export declare function dashboardStyles(): string;
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Dashboard CSS styles for logbook watch hub.
3
+ *
4
+ * Reads from the static hub.css template file instead of embedding CSS
5
+ * in a TypeScript string literal.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { getTemplatesDir } from '../lib/migrations.js';
10
+ /**
11
+ * Read and return the Hub CSS wrapped in a style tag.
12
+ *
13
+ * @returns A `<style>` element string with the full dashboard CSS.
14
+ */
15
+ export function dashboardStyles() {
16
+ const cssPath = join(getTemplatesDir(), 'hub.css');
17
+ const css = readFileSync(cssPath, 'utf8');
18
+ return `<style>\n${css}\n</style>`;
19
+ }
20
+ //# sourceMappingURL=dashboard-styles.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard-styles.js","sourceRoot":"","sources":["../../src/watch/dashboard-styles.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD;;;;GAIG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,OAAO,GAAG,IAAI,CAAC,eAAe,EAAE,EAAE,SAAS,CAAC,CAAC;IACnD,MAAM,GAAG,GAAG,YAAY,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAC1C,OAAO,YAAY,GAAG,YAAY,CAAC;AACrC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Dashboard assembler for logbook watch hub.
3
+ *
4
+ * The "logbook Hub" is the web-based UI for `logbook watch` instances. It
5
+ * composes the full HTML page from separate style, script, and HTML modules.
6
+ */
7
+ /**
8
+ * Generate the full dashboard HTML document.
9
+ *
10
+ * @returns Complete HTML string for the dashboard page.
11
+ */
12
+ export declare function generateDashboardHtml(): Promise<string>;
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Dashboard assembler for logbook watch hub.
3
+ *
4
+ * The "logbook Hub" is the web-based UI for `logbook watch` instances. It
5
+ * composes the full HTML page from separate style, script, and HTML modules.
6
+ */
7
+ import { readFileSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { dashboardStyles } from './dashboard-styles.js';
10
+ import { dashboardScript } from './dashboard-script.js';
11
+ import { dashboardHtml } from './dashboard-html.js';
12
+ import { getTemplatesDir } from '../lib/migrations.js';
13
+ import { mdToHtml } from '../lib/build-helpers.js';
14
+ /**
15
+ * Read the about-watch.md template and return raw markdown content.
16
+ * Falls back to a default message if the file is missing.
17
+ */
18
+ function readAboutMarkdown() {
19
+ try {
20
+ const templatePath = join(getTemplatesDir(), 'about-watch.md');
21
+ return readFileSync(templatePath, 'utf8');
22
+ }
23
+ catch {
24
+ return 'No about-watch.md template found.';
25
+ }
26
+ }
27
+ /**
28
+ * Generate the full dashboard HTML document.
29
+ *
30
+ * @returns Complete HTML string for the dashboard page.
31
+ */
32
+ export async function generateDashboardHtml() {
33
+ const aboutMd = readAboutMarkdown();
34
+ const aboutHtml = await mdToHtml(aboutMd);
35
+ return `<!DOCTYPE html>
36
+ <html lang="en">
37
+ <head>
38
+ <meta charset="utf-8">
39
+ <meta name="viewport" content="width=device-width, initial-scale=1">
40
+ <title>Logbook Hub</title>
41
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 100'%3E%3Crect width='100' height='100' rx='18' fill='%232563eb'/%3E%3Ctext x='50' y='50' fill='%23fff' font-family='Arial,Helvetica,sans-serif' font-size='55' font-weight='700' text-anchor='middle' dominant-baseline='central'%3EHUB%3C/text%3E%3C/svg%3E">
42
+ ${dashboardStyles()}
43
+ </head>
44
+ <body>
45
+ ${dashboardHtml(aboutHtml)}
46
+ ${dashboardScript()}
47
+ </body>
48
+ </html>`;
49
+ }
50
+ //# sourceMappingURL=dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/watch/dashboard.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAEnD;;;GAGG;AACH,SAAS,iBAAiB;IACxB,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,IAAI,CAAC,eAAe,EAAE,EAAE,gBAAgB,CAAC,CAAC;QAC/D,OAAO,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,mCAAmC,CAAC;IAC7C,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,MAAM,OAAO,GAAG,iBAAiB,EAAE,CAAC;IACpC,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,OAAO,CAAC,CAAC;IAE1C,OAAO;;;;;;;IAOL,eAAe,EAAE;;;IAGjB,aAAa,CAAC,SAAS,CAAC;IACxB,eAAe,EAAE;;QAEb,CAAC;AACT,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Shared git utility helpers for logbook watch collectors.
3
+ *
4
+ * Provides a single `git()` runner and common git queries used by multiple
5
+ * collectors, eliminating duplication across collector files.
6
+ */
7
+ /**
8
+ * Parse `git status --porcelain` into staged and unstaged file counts.
9
+ */
10
+ export declare function parseGitStatus(cwd: string): {
11
+ staged: number;
12
+ unstaged: number;
13
+ };
14
+ /**
15
+ * Count commits ahead of the remote tracking branch.
16
+ */
17
+ export declare function commitsAheadOfRemote(cwd: string): number;
18
+ /**
19
+ * Count commits on the current branch vs the default branch.
20
+ */
21
+ export declare function commitsVsDefault(cwd: string, defaultBranch: string): number;
22
+ /** Extract a ticket ID from a branch name using the configured prefix or a generic pattern. */
23
+ export declare function extractTicketFromBranch(branch: string | undefined, jiraPrefix: string | undefined): string | undefined;
24
+ /**
25
+ * Resolve the active ticket ID. Checks lockfile first, then falls back to
26
+ * extracting it from the git branch name.
27
+ */
28
+ export declare function resolveActiveTicketId(cwd: string, branch: string | undefined, jiraPrefix: string | undefined): string | undefined;
29
+ /** Read the full active ticket slug from the lockfile (folder name). */
30
+ export declare function readActiveTicketSlug(cwd: string): string | undefined;
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Shared git utility helpers for logbook watch collectors.
3
+ *
4
+ * Provides a single `git()` runner and common git queries used by multiple
5
+ * collectors, eliminating duplication across collector files.
6
+ */
7
+ import { execSync } from 'node:child_process';
8
+ import { existsSync, readFileSync } from 'node:fs';
9
+ import { join } from 'node:path';
10
+ /**
11
+ * Run a git command and return trimmed stdout, or undefined on failure.
12
+ *
13
+ * @param cwd - Working directory.
14
+ * @param args - Git arguments (e.g., 'status --porcelain').
15
+ * @returns Trimmed output string, or undefined if the command fails or returns empty.
16
+ */
17
+ function git(cwd, args) {
18
+ try {
19
+ return (execSync(`git ${args}`, {
20
+ encoding: 'utf8',
21
+ cwd,
22
+ stdio: ['ignore', 'pipe', 'ignore'],
23
+ }).trim() || undefined);
24
+ }
25
+ catch {
26
+ return undefined;
27
+ }
28
+ }
29
+ /**
30
+ * Parse `git status --porcelain` into staged and unstaged file counts.
31
+ */
32
+ export function parseGitStatus(cwd) {
33
+ const output = git(cwd, 'status --porcelain') ?? '';
34
+ let staged = 0;
35
+ let unstaged = 0;
36
+ for (const line of output.split('\n').filter(Boolean)) {
37
+ const idx = line[0];
38
+ const wt = line[1];
39
+ if (idx && idx !== ' ' && idx !== '?')
40
+ staged++;
41
+ if (wt && wt !== ' ' && wt !== '?')
42
+ unstaged++;
43
+ if (idx === '?' && wt === '?')
44
+ unstaged++;
45
+ }
46
+ return { staged, unstaged };
47
+ }
48
+ /**
49
+ * Count commits ahead of the remote tracking branch.
50
+ */
51
+ export function commitsAheadOfRemote(cwd) {
52
+ const out = git(cwd, 'rev-list --count @{upstream}..HEAD');
53
+ return out ? parseInt(out, 10) : 0;
54
+ }
55
+ /**
56
+ * Count commits on the current branch vs the default branch.
57
+ */
58
+ export function commitsVsDefault(cwd, defaultBranch) {
59
+ const out = git(cwd, `rev-list --count ${defaultBranch}..HEAD`);
60
+ return out ? parseInt(out, 10) : 0;
61
+ }
62
+ /** Read the active ticket slug from the lockfile, if it exists. */
63
+ function readLockfileSlug(cwd) {
64
+ const lockPath = join(cwd, '.logbook-active');
65
+ if (!existsSync(lockPath))
66
+ return undefined;
67
+ try {
68
+ const data = JSON.parse(readFileSync(lockPath, 'utf8'));
69
+ return data.slug || undefined;
70
+ }
71
+ catch {
72
+ return undefined;
73
+ }
74
+ }
75
+ /** Extract a ticket ID (e.g. "IBB-558") from the first two dash-separated segments of a slug. */
76
+ function ticketIdFromSlug(slug) {
77
+ const parts = slug.split('-');
78
+ return parts.length >= 2 ? `${parts[0]}-${parts[1]}` : slug;
79
+ }
80
+ /** Extract a ticket ID from a branch name using the configured prefix or a generic pattern. */
81
+ export function extractTicketFromBranch(branch, jiraPrefix) {
82
+ if (!branch)
83
+ return undefined;
84
+ const regex = jiraPrefix ? new RegExp(`\\b${jiraPrefix}-\\d+\\b`, 'i') : /\b[A-Z]+-\d+\b/;
85
+ return branch.match(regex)?.[0]?.toUpperCase();
86
+ }
87
+ /**
88
+ * Resolve the active ticket ID. Checks lockfile first, then falls back to
89
+ * extracting it from the git branch name.
90
+ */
91
+ export function resolveActiveTicketId(cwd, branch, jiraPrefix) {
92
+ const slug = readLockfileSlug(cwd);
93
+ if (slug)
94
+ return ticketIdFromSlug(slug);
95
+ return extractTicketFromBranch(branch, jiraPrefix);
96
+ }
97
+ /** Read the full active ticket slug from the lockfile (folder name). */
98
+ export function readActiveTicketSlug(cwd) {
99
+ return readLockfileSlug(cwd);
100
+ }
101
+ //# sourceMappingURL=git-utils.js.map