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,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 } 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);
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;
@@ -0,0 +1,90 @@
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 { existsSync, readFileSync } from 'node:fs';
9
+ import matter from 'gray-matter';
10
+ import { getConfig, getLogbookDirPath } from '../../lib/config.js';
11
+ import { getEntryFilePaths } from '../../lib/entry-paths.js';
12
+ import { getLastLogEntry } from '../log-utils.js';
13
+ import { resolveActiveTicketId, readActiveTicketSlug } from '../git-utils.js';
14
+ /** Check if the YAML frontmatter has any unfilled template placeholders. */
15
+ function hasPlaceholders(raw) {
16
+ try {
17
+ const parsed = matter(raw);
18
+ const data = parsed.data ?? {};
19
+ const placeholders = ['[PROMPTER]', '[HARNESS]', '[LLM]', '[WRITE_SUMMARY_HERE]'];
20
+ for (const val of Object.values(data)) {
21
+ if (typeof val === 'string' && placeholders.includes(val))
22
+ return true;
23
+ }
24
+ return false;
25
+ }
26
+ catch {
27
+ return false;
28
+ }
29
+ }
30
+ /** Check if dateEnd is set (i.e., entry is released). */
31
+ function isReleased(raw) {
32
+ try {
33
+ const parsed = matter(raw);
34
+ const dateEnd = parsed.data?.dateEnd;
35
+ if (!dateEnd)
36
+ return false;
37
+ const val = String(dateEnd).trim();
38
+ return val !== '' && val !== '[DATE_END]';
39
+ }
40
+ catch {
41
+ return false;
42
+ }
43
+ }
44
+ export const entriesCollector = {
45
+ name: 'entries',
46
+ schedule: { type: 'fs', patterns: ['**/log.md', '**/index.md', '**/ticket.md', '.logbook-active'] },
47
+ async available(_cwd) {
48
+ return true;
49
+ },
50
+ async collect(cwd, state) {
51
+ const config = getConfig(cwd);
52
+ const entriesDir = getLogbookDirPath(config, cwd);
53
+ const slug = readActiveTicketSlug(cwd);
54
+ if (slug) {
55
+ state.hasActiveTicket = true;
56
+ state.hasLockfile = true;
57
+ state.activeTicketSlug = slug;
58
+ state.activeTicketId = resolveActiveTicketId(cwd, state.branch, config.jiraPrefix);
59
+ const { log: logPath } = getEntryFilePaths(entriesDir, slug);
60
+ const logEntry = getLastLogEntry(logPath);
61
+ if (logEntry) {
62
+ state.latestLog = logEntry.message;
63
+ state.latestLogTimestamp = logEntry.timestamp;
64
+ state.lastActivityTimestamp = logEntry.timestamp;
65
+ }
66
+ const { index: indexPath } = getEntryFilePaths(entriesDir, slug);
67
+ if (existsSync(indexPath)) {
68
+ const raw = readFileSync(indexPath, 'utf8');
69
+ state.hasNarrativePlaceholders = hasPlaceholders(raw);
70
+ state.isReleased = isReleased(raw);
71
+ }
72
+ else {
73
+ state.hasNarrativePlaceholders = true;
74
+ state.isReleased = false;
75
+ }
76
+ }
77
+ else {
78
+ state.hasActiveTicket = false;
79
+ state.hasLockfile = false;
80
+ state.activeTicketSlug = undefined;
81
+ state.latestLog = undefined;
82
+ state.latestLogTimestamp = undefined;
83
+ state.hasNarrativePlaceholders = false;
84
+ state.isReleased = false;
85
+ state.activeTicketId = resolveActiveTicketId(cwd, state.branch, config.jiraPrefix);
86
+ state.hasActiveTicket = !!state.activeTicketId;
87
+ }
88
+ },
89
+ };
90
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.js","sourceRoot":"","sources":["../../../src/watch/collectors/entries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Git collector for logbook watch.
3
+ *
4
+ * Collects branch information, dirty state, and ahead/behind counts. Runs on
5
+ * file-system events (instant feedback when files change) and on interval
6
+ * (to catch remote state changes).
7
+ *
8
+ * Uses shared git-utils helpers to avoid duplication with the detail
9
+ * collector and other collectors.
10
+ */
11
+ import type { Collector } from './types.js';
12
+ export declare const gitCollector: Collector;
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Git collector for logbook watch.
3
+ *
4
+ * Collects branch information, dirty state, and ahead/behind counts. Runs on
5
+ * file-system events (instant feedback when files change) and on interval
6
+ * (to catch remote state changes).
7
+ *
8
+ * Uses shared git-utils helpers to avoid duplication with the detail
9
+ * collector and other collectors.
10
+ */
11
+ import { getCurrentBranch, getDefaultBranch } from '../../lib/git-helpers.js';
12
+ import { parseGitStatus, commitsAheadOfRemote, commitsVsDefault } from '../git-utils.js';
13
+ /** Check if a git repo exists at cwd. */
14
+ async function isGitRepo(cwd) {
15
+ try {
16
+ const { execSync } = await import('node:child_process');
17
+ execSync('git rev-parse --git-dir', {
18
+ cwd,
19
+ stdio: ['ignore', 'pipe', 'ignore'],
20
+ });
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ export const gitCollector = {
28
+ name: 'git',
29
+ schedule: { type: 'fs', patterns: ['**/*'] },
30
+ async available(cwd) {
31
+ return isGitRepo(cwd);
32
+ },
33
+ async collect(cwd, state) {
34
+ const branch = getCurrentBranch(cwd);
35
+ state.branch = branch;
36
+ const defaultBranch = getDefaultBranch(cwd);
37
+ state.defaultBranch = defaultBranch;
38
+ if (branch) {
39
+ const { staged, unstaged } = parseGitStatus(cwd);
40
+ state.stagedFiles = staged;
41
+ state.unstagedFiles = unstaged;
42
+ state.isDirty = staged > 0 || unstaged > 0;
43
+ state.isAheadOfRemote = commitsAheadOfRemote(cwd) > 0;
44
+ state.commitsBehindDefault = commitsVsDefault(cwd, defaultBranch);
45
+ }
46
+ else {
47
+ state.stagedFiles = 0;
48
+ state.unstagedFiles = 0;
49
+ state.isDirty = false;
50
+ state.isAheadOfRemote = false;
51
+ state.commitsBehindDefault = 0;
52
+ }
53
+ if (!state.lastActivityTimestamp) {
54
+ state.lastActivityTimestamp = new Date().toISOString();
55
+ }
56
+ },
57
+ };
58
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/watch/collectors/git.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG9E,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAEzF,yCAAyC;AACzC,KAAK,UAAU,SAAS,CAAC,GAAW;IAClC,IAAI,CAAC;QACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACxD,QAAQ,CAAC,yBAAyB,EAAE;YAClC,GAAG;YACH,KAAK,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC;SACpC,CAAC,CAAC;QACH,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,YAAY,GAAc;IACrC,IAAI,EAAE,KAAK;IAEX,QAAQ,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;IAE5C,KAAK,CAAC,SAAS,CAAC,GAAW;QACzB,OAAO,SAAS,CAAC,GAAG,CAAC,CAAC;IACxB,CAAC;IAED,KAAK,CAAC,OAAO,CAAC,GAAW,EAAE,KAAmB;QAC5C,MAAM,MAAM,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACrC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;QAEtB,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAC5C,KAAK,CAAC,aAAa,GAAG,aAAa,CAAC;QAEpC,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;YACjD,KAAK,CAAC,WAAW,GAAG,MAAM,CAAC;YAC3B,KAAK,CAAC,aAAa,GAAG,QAAQ,CAAC;YAC/B,KAAK,CAAC,OAAO,GAAG,MAAM,GAAG,CAAC,IAAI,QAAQ,GAAG,CAAC,CAAC;YAE3C,KAAK,CAAC,eAAe,GAAG,oBAAoB,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YACtD,KAAK,CAAC,oBAAoB,GAAG,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;QACpE,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,WAAW,GAAG,CAAC,CAAC;YACtB,KAAK,CAAC,aAAa,GAAG,CAAC,CAAC;YACxB,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;YACtB,KAAK,CAAC,eAAe,GAAG,KAAK,CAAC;YAC9B,KAAK,CAAC,oBAAoB,GAAG,CAAC,CAAC;QACjC,CAAC;QAED,IAAI,CAAC,KAAK,CAAC,qBAAqB,EAAE,CAAC;YACjC,KAAK,CAAC,qBAAqB,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACzD,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * GitLab collector for logbook watch.
3
+ *
4
+ * Uses the `glab` CLI to query merge requests and pipeline status for the
5
+ * current branch. Runs on interval (30s) since it calls an external API.
6
+ *
7
+ * Falls back gracefully when `glab` is not installed or not authenticated.
8
+ */
9
+ import type { Collector } from './types.js';
10
+ export declare const glabCollector: Collector;
@@ -0,0 +1,184 @@
1
+ /**
2
+ * GitLab collector for logbook watch.
3
+ *
4
+ * Uses the `glab` CLI to query merge requests and pipeline status for the
5
+ * current branch. Runs on interval (30s) since it calls an external API.
6
+ *
7
+ * Falls back gracefully when `glab` is not installed or not authenticated.
8
+ */
9
+ import { execSync } from 'node:child_process';
10
+ import { getCurrentBranch } from '../../lib/git-helpers.js';
11
+ import { getDefaultBranch } from '../../lib/git-helpers.js';
12
+ import { parsePipelineStatus } from './type-guards.js';
13
+ /** Check if glab CLI is available and authenticated. */
14
+ async function isGlabAvailable() {
15
+ try {
16
+ execSync('glab --version', {
17
+ stdio: ['ignore', 'pipe', 'ignore'],
18
+ });
19
+ // Check auth status — "glab auth status" exits 0 when logged in.
20
+ execSync('glab auth status', {
21
+ stdio: ['ignore', 'pipe', 'ignore'],
22
+ });
23
+ return true;
24
+ }
25
+ catch {
26
+ return false;
27
+ }
28
+ }
29
+ /** Fetch MRs from glab for the current branch, returning parsed items. */
30
+ function fetchMrs(cwd) {
31
+ try {
32
+ const output = execSync('glab mr list --output json --state opened --per-page 100', {
33
+ encoding: 'utf8',
34
+ cwd,
35
+ stdio: ['ignore', 'pipe', 'ignore'],
36
+ }).trim();
37
+ if (!output)
38
+ return [];
39
+ const items = JSON.parse(output);
40
+ return items;
41
+ }
42
+ catch {
43
+ return [];
44
+ }
45
+ }
46
+ /** Fetch pipeline status for the current branch. */
47
+ function fetchPipelineStatus(cwd, branch) {
48
+ try {
49
+ const output = execSync(`glab ci status --branch "${branch}" --output json`, {
50
+ encoding: 'utf8',
51
+ cwd,
52
+ stdio: ['ignore', 'pipe', 'ignore'],
53
+ }).trim();
54
+ if (!output)
55
+ return 'none';
56
+ const parsed = JSON.parse(output);
57
+ return parsePipelineStatus(parsed.status);
58
+ }
59
+ catch {
60
+ return 'none';
61
+ }
62
+ }
63
+ /** Check if branch has recent running/failed pipelines via glab pipeline list. */
64
+ function hasRecentPipeline(cwd, branch, status) {
65
+ try {
66
+ const output = execSync(`glab pipeline list --branch "${branch}" --output json --per-page 5`, {
67
+ encoding: 'utf8',
68
+ cwd,
69
+ stdio: ['ignore', 'pipe', 'ignore'],
70
+ }).trim();
71
+ if (!output)
72
+ return false;
73
+ const pipelines = JSON.parse(output);
74
+ return pipelines.some((p) => p.status === status);
75
+ }
76
+ catch {
77
+ return false;
78
+ }
79
+ }
80
+ /** Determine MR status from a glab MR item. */
81
+ function determineMrStatus(mr, _branch) {
82
+ if (mr.state === 'merged')
83
+ return 'merged';
84
+ if (mr.state === 'closed')
85
+ return 'none';
86
+ // Draft check (GitLab 14+)
87
+ if (mr.draft === true)
88
+ return 'draft';
89
+ // Conflicts detection
90
+ if (mr.conflicts)
91
+ return 'open';
92
+ // Approval-based status
93
+ const approvalsLeft = mr.approvals_left ?? 0;
94
+ const approvalsRequired = mr.approvals_required ?? 0;
95
+ const changesRequested = (mr.reviewers ?? []).some((r) => r.state === 'CHANGED');
96
+ if (approvalsRequired > 0 && approvalsLeft === 0 && !changesRequested) {
97
+ return 'approved';
98
+ }
99
+ if (approvalsRequired === 0) {
100
+ return 'approval-optional';
101
+ }
102
+ if (changesRequested) {
103
+ return 'changes-requested';
104
+ }
105
+ // Open comments detection
106
+ if (mr.merge_status === 'cannot_be_merged') {
107
+ return 'open';
108
+ }
109
+ return 'open';
110
+ }
111
+ export const glabCollector = {
112
+ name: 'glab',
113
+ schedule: { type: 'interval', ms: 30_000 },
114
+ async available(_cwd) {
115
+ return isGlabAvailable();
116
+ },
117
+ async collect(cwd, state) {
118
+ const branch = getCurrentBranch(cwd);
119
+ if (!branch)
120
+ return;
121
+ try {
122
+ const mrs = fetchMrs(cwd)
123
+ .filter((mr) => mr.source_branch === branch)
124
+ .sort((a, b) => b.iid - a.iid);
125
+ if (mrs.length === 0) {
126
+ state.mrStatus = 'none';
127
+ state.mrUrl = undefined;
128
+ state.mrTitle = undefined;
129
+ state.mrOpenThreads = undefined;
130
+ state.mrHasConflicts = undefined;
131
+ state.glabOpenMrs = [];
132
+ state.glabLastUpdated = new Date().toISOString();
133
+ return;
134
+ }
135
+ const mr = mrs[0];
136
+ // Check pipeline status via mr head_pipeline first, fall back to ci status
137
+ if (mr.head_pipeline) {
138
+ state.pipelineStatus = parsePipelineStatus(mr.head_pipeline.status);
139
+ }
140
+ else {
141
+ state.pipelineStatus = fetchPipelineStatus(cwd, branch);
142
+ }
143
+ // Fallback: check for recent running/failed pipelines if head_pipeline is stale
144
+ if (state.pipelineStatus === 'none' || state.pipelineStatus === 'passed') {
145
+ const defaultBranch = getDefaultBranch(cwd);
146
+ if (branch !== defaultBranch) {
147
+ if (hasRecentPipeline(cwd, branch, 'running')) {
148
+ state.pipelineStatus = 'running';
149
+ }
150
+ else if (hasRecentPipeline(cwd, branch, 'failed')) {
151
+ state.pipelineStatus = 'failed';
152
+ }
153
+ }
154
+ }
155
+ state.mrStatus = determineMrStatus(mr, branch);
156
+ state.mrUrl = mr.web_url;
157
+ state.mrTitle = mr.title;
158
+ state.mrHasConflicts = mr.conflicts;
159
+ // Count open threads (note_count - resolved)
160
+ state.mrOpenThreads = 0;
161
+ // Build full MR list for the GitLab tab (all open MRs for the project).
162
+ const allMrs = fetchMrs(cwd);
163
+ const projectUrl = allMrs[0]?.web_url ? allMrs[0].web_url.replace(/\/-\/merge_requests\/\d+$/, '') : '';
164
+ state.glabOpenMrs = allMrs.map((item) => ({
165
+ iid: item.iid,
166
+ title: item.title,
167
+ status: item.state,
168
+ comments: 0,
169
+ url: item.web_url,
170
+ sourceBranch: item.source_branch,
171
+ projectUrl: projectUrl ? projectUrl : item.web_url.replace(/\/-\/merge_requests\/\d+$/, ''),
172
+ projectName: projectUrl
173
+ .split('/')
174
+ .pop()
175
+ ?.replace(/\.git$/, '') ?? '',
176
+ }));
177
+ state.glabLastUpdated = new Date().toISOString();
178
+ }
179
+ catch {
180
+ // Collector failed — state retains previous values.
181
+ }
182
+ },
183
+ };
184
+ //# sourceMappingURL=glab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"glab.js","sourceRoot":"","sources":["../../../src/watch/collectors/glab.ts"],"names":[],"mappings":"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"}