project-logbook 1.1.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 (209) hide show
  1. package/README.md +5 -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 +25 -0
  7. package/dist/commands/new-helpers.js +106 -0
  8. package/dist/commands/new-helpers.js.map +1 -0
  9. package/dist/commands/new.js +91 -92
  10. package/dist/commands/new.js.map +1 -1
  11. package/dist/commands/release.js +2 -0
  12. package/dist/commands/release.js.map +1 -1
  13. package/dist/commands/start.d.ts +1 -1
  14. package/dist/commands/start.js +154 -42
  15. package/dist/commands/start.js.map +1 -1
  16. package/dist/commands/status.js +3 -0
  17. package/dist/commands/status.js.map +1 -1
  18. package/dist/commands/watch.d.ts +15 -0
  19. package/dist/commands/watch.js +153 -0
  20. package/dist/commands/watch.js.map +1 -0
  21. package/dist/index.js +8 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/lib/autolink.d.ts +5 -0
  24. package/dist/lib/autolink.js +36 -2
  25. package/dist/lib/autolink.js.map +1 -1
  26. package/dist/lib/build-helpers.d.ts +12 -3
  27. package/dist/lib/build-helpers.js +70 -65
  28. package/dist/lib/build-helpers.js.map +1 -1
  29. package/dist/lib/build-steps.js +2 -1
  30. package/dist/lib/build-steps.js.map +1 -1
  31. package/dist/lib/config.d.ts +8 -0
  32. package/dist/lib/config.js +7 -0
  33. package/dist/lib/config.js.map +1 -1
  34. package/dist/lib/custom-card-renderer.js +2 -1
  35. package/dist/lib/custom-card-renderer.js.map +1 -1
  36. package/dist/lib/entry-href.d.ts +7 -0
  37. package/dist/lib/entry-href.js +8 -0
  38. package/dist/lib/entry-href.js.map +1 -0
  39. package/dist/lib/markdown-processors.d.ts +6 -2
  40. package/dist/lib/markdown-processors.js +9 -7
  41. package/dist/lib/markdown-processors.js.map +1 -1
  42. package/dist/lib/post-template.js +3 -3
  43. package/dist/lib/post-template.js.map +1 -1
  44. package/dist/lib/template-helpers.js +3 -2
  45. package/dist/lib/template-helpers.js.map +1 -1
  46. package/dist/lib/template-types.d.ts +2 -0
  47. package/dist/lib/templates.js +5 -3
  48. package/dist/lib/templates.js.map +1 -1
  49. package/dist/linters/jira-prefix.js +32 -16
  50. package/dist/linters/jira-prefix.js.map +1 -1
  51. package/dist/templates/CONTRIBUTING.md +35 -3
  52. package/dist/templates/about-watch.md +29 -0
  53. package/dist/templates/adr.md +34 -0
  54. package/dist/templates/hub.css +677 -0
  55. package/dist/templates/hub.js +628 -0
  56. package/dist/templates/index.md +5 -4
  57. package/dist/templates/steer.txt +14 -1
  58. package/dist/utils/id.d.ts +1 -1
  59. package/dist/utils/id.js +4 -1
  60. package/dist/utils/id.js.map +1 -1
  61. package/dist/utils/prompt.d.ts +6 -0
  62. package/dist/utils/prompt.js +59 -0
  63. package/dist/utils/prompt.js.map +1 -0
  64. package/dist/watch/collector.d.ts +14 -0
  65. package/dist/watch/collector.js +33 -0
  66. package/dist/watch/collector.js.map +1 -0
  67. package/dist/watch/collectors/acli.d.ts +15 -0
  68. package/dist/watch/collectors/acli.js +197 -0
  69. package/dist/watch/collectors/acli.js.map +1 -0
  70. package/dist/watch/collectors/entries.d.ts +9 -0
  71. package/dist/watch/collectors/entries.js +90 -0
  72. package/dist/watch/collectors/entries.js.map +1 -0
  73. package/dist/watch/collectors/git.d.ts +12 -0
  74. package/dist/watch/collectors/git.js +58 -0
  75. package/dist/watch/collectors/git.js.map +1 -0
  76. package/dist/watch/collectors/glab.d.ts +10 -0
  77. package/dist/watch/collectors/glab.js +184 -0
  78. package/dist/watch/collectors/glab.js.map +1 -0
  79. package/dist/watch/collectors/index.d.ts +10 -0
  80. package/dist/watch/collectors/index.js +21 -0
  81. package/dist/watch/collectors/index.js.map +1 -0
  82. package/dist/watch/collectors/jira.d.ts +3 -0
  83. package/dist/watch/collectors/jira.js +110 -0
  84. package/dist/watch/collectors/jira.js.map +1 -0
  85. package/dist/watch/collectors/type-guards.d.ts +17 -0
  86. package/dist/watch/collectors/type-guards.js +35 -0
  87. package/dist/watch/collectors/type-guards.js.map +1 -0
  88. package/dist/watch/collectors/types.d.ts +37 -0
  89. package/dist/watch/collectors/types.js +9 -0
  90. package/dist/watch/collectors/types.js.map +1 -0
  91. package/dist/watch/dashboard-html.d.ts +11 -0
  92. package/dist/watch/dashboard-html.js +73 -0
  93. package/dist/watch/dashboard-html.js.map +1 -0
  94. package/dist/watch/dashboard-script.d.ts +12 -0
  95. package/dist/watch/dashboard-script.js +20 -0
  96. package/dist/watch/dashboard-script.js.map +1 -0
  97. package/dist/watch/dashboard-styles.d.ts +12 -0
  98. package/dist/watch/dashboard-styles.js +20 -0
  99. package/dist/watch/dashboard-styles.js.map +1 -0
  100. package/dist/watch/dashboard.d.ts +12 -0
  101. package/dist/watch/dashboard.js +50 -0
  102. package/dist/watch/dashboard.js.map +1 -0
  103. package/dist/watch/git-utils.d.ts +30 -0
  104. package/dist/watch/git-utils.js +101 -0
  105. package/dist/watch/git-utils.js.map +1 -0
  106. package/dist/watch/hub-state.d.ts +37 -0
  107. package/dist/watch/hub-state.js +55 -0
  108. package/dist/watch/hub-state.js.map +1 -0
  109. package/dist/watch/hub.d.ts +2 -0
  110. package/dist/watch/hub.js +219 -0
  111. package/dist/watch/hub.js.map +1 -0
  112. package/dist/watch/log-utils.d.ts +13 -0
  113. package/dist/watch/log-utils.js +24 -0
  114. package/dist/watch/log-utils.js.map +1 -0
  115. package/dist/watch/notify.d.ts +18 -0
  116. package/dist/watch/notify.js +45 -0
  117. package/dist/watch/notify.js.map +1 -0
  118. package/dist/watch/state.d.ts +34 -0
  119. package/dist/watch/state.js +36 -0
  120. package/dist/watch/state.js.map +1 -0
  121. package/dist/watch/strategies/behind-default.d.ts +5 -0
  122. package/dist/watch/strategies/behind-default.js +21 -0
  123. package/dist/watch/strategies/behind-default.js.map +1 -0
  124. package/dist/watch/strategies/idle.d.ts +5 -0
  125. package/dist/watch/strategies/idle.js +21 -0
  126. package/dist/watch/strategies/idle.js.map +1 -0
  127. package/dist/watch/strategies/in-progress.d.ts +5 -0
  128. package/dist/watch/strategies/in-progress.js +21 -0
  129. package/dist/watch/strategies/in-progress.js.map +1 -0
  130. package/dist/watch/strategies/index.d.ts +34 -0
  131. package/dist/watch/strategies/index.js +148 -0
  132. package/dist/watch/strategies/index.js.map +1 -0
  133. package/dist/watch/strategies/jira-not-in-review.d.ts +2 -0
  134. package/dist/watch/strategies/jira-not-in-review.js +29 -0
  135. package/dist/watch/strategies/jira-not-in-review.js.map +1 -0
  136. package/dist/watch/strategies/jira-still-open.d.ts +2 -0
  137. package/dist/watch/strategies/jira-still-open.js +31 -0
  138. package/dist/watch/strategies/jira-still-open.js.map +1 -0
  139. package/dist/watch/strategies/mr-approval-optional.d.ts +5 -0
  140. package/dist/watch/strategies/mr-approval-optional.js +21 -0
  141. package/dist/watch/strategies/mr-approval-optional.js.map +1 -0
  142. package/dist/watch/strategies/mr-approved.d.ts +5 -0
  143. package/dist/watch/strategies/mr-approved.js +21 -0
  144. package/dist/watch/strategies/mr-approved.js.map +1 -0
  145. package/dist/watch/strategies/mr-conflicts.d.ts +5 -0
  146. package/dist/watch/strategies/mr-conflicts.js +21 -0
  147. package/dist/watch/strategies/mr-conflicts.js.map +1 -0
  148. package/dist/watch/strategies/mr-draft.d.ts +5 -0
  149. package/dist/watch/strategies/mr-draft.js +21 -0
  150. package/dist/watch/strategies/mr-draft.js.map +1 -0
  151. package/dist/watch/strategies/mr-open-comments.d.ts +5 -0
  152. package/dist/watch/strategies/mr-open-comments.js +21 -0
  153. package/dist/watch/strategies/mr-open-comments.js.map +1 -0
  154. package/dist/watch/strategies/narrative-missing.d.ts +5 -0
  155. package/dist/watch/strategies/narrative-missing.js +21 -0
  156. package/dist/watch/strategies/narrative-missing.js.map +1 -0
  157. package/dist/watch/strategies/no-mr.d.ts +5 -0
  158. package/dist/watch/strategies/no-mr.js +21 -0
  159. package/dist/watch/strategies/no-mr.js.map +1 -0
  160. package/dist/watch/strategies/pipeline-failed.d.ts +5 -0
  161. package/dist/watch/strategies/pipeline-failed.js +21 -0
  162. package/dist/watch/strategies/pipeline-failed.js.map +1 -0
  163. package/dist/watch/strategies/pipeline-running.d.ts +5 -0
  164. package/dist/watch/strategies/pipeline-running.js +21 -0
  165. package/dist/watch/strategies/pipeline-running.js.map +1 -0
  166. package/dist/watch/strategies/ready-for-release.d.ts +7 -0
  167. package/dist/watch/strategies/ready-for-release.js +35 -0
  168. package/dist/watch/strategies/ready-for-release.js.map +1 -0
  169. package/dist/watch/strategies/ready-to-commit.d.ts +5 -0
  170. package/dist/watch/strategies/ready-to-commit.js +21 -0
  171. package/dist/watch/strategies/ready-to-commit.js.map +1 -0
  172. package/dist/watch/strategies/ready-to-push.d.ts +5 -0
  173. package/dist/watch/strategies/ready-to-push.js +21 -0
  174. package/dist/watch/strategies/ready-to-push.js.map +1 -0
  175. package/dist/watch/strategies/released.d.ts +6 -0
  176. package/dist/watch/strategies/released.js +21 -0
  177. package/dist/watch/strategies/released.js.map +1 -0
  178. package/dist/watch/strategies/squash-merged.d.ts +6 -0
  179. package/dist/watch/strategies/squash-merged.js +21 -0
  180. package/dist/watch/strategies/squash-merged.js.map +1 -0
  181. package/dist/watch/strategies/types.d.ts +31 -0
  182. package/dist/watch/strategies/types.js +12 -0
  183. package/dist/watch/strategies/types.js.map +1 -0
  184. package/dist/watch/strategies/unstaged-changes.d.ts +5 -0
  185. package/dist/watch/strategies/unstaged-changes.js +21 -0
  186. package/dist/watch/strategies/unstaged-changes.js.map +1 -0
  187. package/dist/watch/strategies/wrong-branch.d.ts +6 -0
  188. package/dist/watch/strategies/wrong-branch.js +26 -0
  189. package/dist/watch/strategies/wrong-branch.js.map +1 -0
  190. package/dist/watch/types.d.ts +190 -0
  191. package/dist/watch/types.js +25 -0
  192. package/dist/watch/types.js.map +1 -0
  193. package/dist/watch/watcher-fs.d.ts +39 -0
  194. package/dist/watch/watcher-fs.js +105 -0
  195. package/dist/watch/watcher-fs.js.map +1 -0
  196. package/dist/watch/watcher-http.d.ts +19 -0
  197. package/dist/watch/watcher-http.js +47 -0
  198. package/dist/watch/watcher-http.js.map +1 -0
  199. package/dist/watch/watcher.d.ts +4 -0
  200. package/dist/watch/watcher.js +174 -0
  201. package/dist/watch/watcher.js.map +1 -0
  202. package/package.json +3 -2
  203. package/src/templates/CONTRIBUTING.md +35 -3
  204. package/src/templates/about-watch.md +29 -0
  205. package/src/templates/adr.md +34 -0
  206. package/src/templates/hub.css +677 -0
  207. package/src/templates/hub.js +628 -0
  208. package/src/templates/index.md +5 -4
  209. package/src/templates/steer.txt +14 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/watch/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA0JH,kDAAkD;AAClD,MAAM,kBAAkB,GAAG,IAAI,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB;IAC9B,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC;IAC/C,IAAI,OAAO,EAAE,CAAC;QACZ,MAAM,MAAM,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QACrC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,MAAM,GAAG,CAAC,IAAI,MAAM,GAAG,KAAK;YAAE,OAAO,MAAM,CAAC;IACpE,CAAC;IACD,OAAO,kBAAkB,CAAC;AAC5B,CAAC"}
@@ -0,0 +1,39 @@
1
+ /**
2
+ * File-system watcher setup for logbook watch.
3
+ *
4
+ * Handles debounced file-system events that trigger collector re-runs.
5
+ */
6
+ import { watch as fsWatch } from 'node:fs';
7
+ import type { Collector } from './collectors/types.js';
8
+ import type { WatcherState } from './state.js';
9
+ /** Context needed by the file-watcher to trigger collections and pushes. */
10
+ interface FsWatcherContext {
11
+ cwd: string;
12
+ port: number;
13
+ entriesDir: string;
14
+ lockfilePath: string;
15
+ activeCollectors: Collector[];
16
+ state: WatcherState;
17
+ /** Callback to push a full status snapshot to the hub. */
18
+ pushStatus: (_reason: string) => Promise<void>;
19
+ }
20
+ /** Handles returned by setupFileWatchers for cleanup. */
21
+ interface FsWatcherHandles {
22
+ /** Native fs.watch handles to close on shutdown. */
23
+ watchers: ReturnType<typeof fsWatch>[];
24
+ /** Debounce timer reference (may be null). */
25
+ getDebounceTimer: () => ReturnType<typeof setTimeout> | null;
26
+ /** Clear the debounce timer on shutdown. */
27
+ clearDebounce: () => void;
28
+ }
29
+ /**
30
+ * Set up file watchers for fs-triggered collectors.
31
+ *
32
+ * Watches the lockfile and entries directory for changes, debounces events,
33
+ * then re-runs fs-triggered collectors and pushes status to the hub.
34
+ *
35
+ * @param ctx - Watcher context with project paths, collectors, and state.
36
+ * @returns Handles for cleanup (watchers to close, timer to clear).
37
+ */
38
+ export declare function setupFileWatchers(ctx: FsWatcherContext): FsWatcherHandles;
39
+ export {};
@@ -0,0 +1,105 @@
1
+ /**
2
+ * File-system watcher setup for logbook watch.
3
+ *
4
+ * Handles debounced file-system events that trigger collector re-runs.
5
+ */
6
+ import { watch as fsWatch, existsSync } from 'node:fs';
7
+ import { postToHub } from './watcher-http.js';
8
+ /** Debounce interval for file watcher events (500ms). */
9
+ const DEBOUNCE_MS = 500;
10
+ /**
11
+ * Set up file watchers for fs-triggered collectors.
12
+ *
13
+ * Watches the lockfile and entries directory for changes, debounces events,
14
+ * then re-runs fs-triggered collectors and pushes status to the hub.
15
+ *
16
+ * @param ctx - Watcher context with project paths, collectors, and state.
17
+ * @returns Handles for cleanup (watchers to close, timer to clear).
18
+ */
19
+ export function setupFileWatchers(ctx) {
20
+ const { cwd, port, entriesDir, lockfilePath, activeCollectors, state, pushStatus } = ctx;
21
+ const fsWatchers = [];
22
+ let debounceTimer = null;
23
+ let lastKnownLog = state.latestLog;
24
+ const debouncedCollectAndPush = (reason) => {
25
+ if (debounceTimer)
26
+ clearTimeout(debounceTimer);
27
+ debounceTimer = setTimeout(async () => {
28
+ // Run all fs-triggered collectors.
29
+ for (const c of activeCollectors.filter((c) => c.schedule.type === 'fs')) {
30
+ await c.collect(cwd, state);
31
+ }
32
+ // If latestLog changed, emit an activity event for the timeline.
33
+ if (state.latestLog && state.latestLog !== lastKnownLog) {
34
+ lastKnownLog = state.latestLog;
35
+ const event = {
36
+ projectPath: cwd,
37
+ timestamp: state.latestLogTimestamp ?? new Date().toISOString(),
38
+ description: state.latestLog,
39
+ source: 'logbook log',
40
+ };
41
+ try {
42
+ await postToHub(port, '/_logbook/event', event);
43
+ }
44
+ catch {
45
+ // Hub may have died.
46
+ }
47
+ }
48
+ try {
49
+ await pushStatus(reason);
50
+ }
51
+ catch {
52
+ // Hub may have died.
53
+ }
54
+ }, DEBOUNCE_MS);
55
+ };
56
+ // Watch the lockfile if it exists.
57
+ try {
58
+ if (existsSync(lockfilePath)) {
59
+ const w = fsWatch(lockfilePath, () => {
60
+ debouncedCollectAndPush('Lockfile changed — ticket started or released');
61
+ });
62
+ fsWatchers.push(w);
63
+ }
64
+ const cwdWatcher = fsWatch(cwd, (_, filename) => {
65
+ if (filename === '.logbook-active') {
66
+ debouncedCollectAndPush('Lockfile created or removed — ticket state changed');
67
+ }
68
+ });
69
+ fsWatchers.push(cwdWatcher);
70
+ }
71
+ catch {
72
+ // fs.watch not supported — fall back to polling only.
73
+ }
74
+ // Watch the entries directory recursively.
75
+ try {
76
+ if (existsSync(entriesDir)) {
77
+ const w = fsWatch(entriesDir, { recursive: true }, (_, filename) => {
78
+ if (!filename)
79
+ return;
80
+ if (filename.endsWith('log.md')) {
81
+ debouncedCollectAndPush(`Log updated: ${filename}`);
82
+ }
83
+ else if (filename.endsWith('index.md')) {
84
+ debouncedCollectAndPush(`Narrative updated: ${filename}`);
85
+ }
86
+ else if (filename.endsWith('ticket.md')) {
87
+ debouncedCollectAndPush(`Ticket updated: ${filename}`);
88
+ }
89
+ });
90
+ fsWatchers.push(w);
91
+ }
92
+ }
93
+ catch {
94
+ // Recursive watch not supported.
95
+ }
96
+ return {
97
+ watchers: fsWatchers,
98
+ getDebounceTimer: () => debounceTimer,
99
+ clearDebounce: () => {
100
+ if (debounceTimer)
101
+ clearTimeout(debounceTimer);
102
+ },
103
+ };
104
+ }
105
+ //# sourceMappingURL=watcher-fs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher-fs.js","sourceRoot":"","sources":["../../src/watch/watcher-fs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * HTTP utilities for the logbook watcher.
3
+ *
4
+ * Provides the hub communication helper and terminal logging timestamp.
5
+ */
6
+ /**
7
+ * Send a JSON POST request to the hub.
8
+ *
9
+ * @param port - Hub port number.
10
+ * @param path - URL path (e.g., '/_logbook/status').
11
+ * @param data - JSON-serializable payload.
12
+ */
13
+ export declare function postToHub(port: number, path: string, data: unknown): Promise<void>;
14
+ /**
15
+ * Format a short timestamp for terminal logging (HH:MM:SS).
16
+ *
17
+ * @returns Formatted time string.
18
+ */
19
+ export declare function logTime(): string;
@@ -0,0 +1,47 @@
1
+ /**
2
+ * HTTP utilities for the logbook watcher.
3
+ *
4
+ * Provides the hub communication helper and terminal logging timestamp.
5
+ */
6
+ import { request } from 'node:http';
7
+ /**
8
+ * Send a JSON POST request to the hub.
9
+ *
10
+ * @param port - Hub port number.
11
+ * @param path - URL path (e.g., '/_logbook/status').
12
+ * @param data - JSON-serializable payload.
13
+ */
14
+ export function postToHub(port, path, data) {
15
+ const body = JSON.stringify(data);
16
+ return new Promise((resolve, reject) => {
17
+ const req = request({
18
+ hostname: '127.0.0.1',
19
+ port,
20
+ path,
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ 'Content-Length': Buffer.byteLength(body),
25
+ },
26
+ }, (res) => {
27
+ res.resume();
28
+ res.on('end', resolve);
29
+ });
30
+ req.on('error', reject);
31
+ req.write(body);
32
+ req.end();
33
+ });
34
+ }
35
+ /**
36
+ * Format a short timestamp for terminal logging (HH:MM:SS).
37
+ *
38
+ * @returns Formatted time string.
39
+ */
40
+ export function logTime() {
41
+ const now = new Date();
42
+ const h = String(now.getHours()).padStart(2, '0');
43
+ const m = String(now.getMinutes()).padStart(2, '0');
44
+ const s = String(now.getSeconds()).padStart(2, '0');
45
+ return `${h}:${m}:${s}`;
46
+ }
47
+ //# sourceMappingURL=watcher-http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher-http.js","sourceRoot":"","sources":["../../src/watch/watcher-http.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC;;;;;;GAMG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY,EAAE,IAAY,EAAE,IAAa;IACjE,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;QACrC,MAAM,GAAG,GAAG,OAAO,CACjB;YACE,QAAQ,EAAE,WAAW;YACrB,IAAI;YACJ,IAAI;YACJ,MAAM,EAAE,MAAM;YACd,OAAO,EAAE;gBACP,cAAc,EAAE,kBAAkB;gBAClC,gBAAgB,EAAE,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC;aAC1C;SACF,EACD,CAAC,GAAG,EAAE,EAAE;YACN,GAAG,CAAC,MAAM,EAAE,CAAC;YACb,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACzB,CAAC,CACF,CAAC;QACF,GAAG,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACxB,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAChB,GAAG,CAAC,GAAG,EAAE,CAAC;IACZ,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,OAAO;IACrB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IACvB,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,MAAM,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IACpD,OAAO,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC1B,CAAC"}
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Start the watcher loop for the given project.
3
+ */
4
+ export declare function startWatcher(cwd: string, port: number): Promise<void>;
@@ -0,0 +1,174 @@
1
+ /** Local project watcher — pushes status snapshots to the hub. */
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import matter from 'gray-matter';
5
+ import { getConfig, getLogbookDirPath } from '../lib/config.js';
6
+ import { getEntryFilePaths } from '../lib/entry-paths.js';
7
+ import { mdToHtml } from '../lib/build-helpers.js';
8
+ import { transformLogToTimeline } from '../utils/log-timeline.js';
9
+ import { info, success } from '../lib/theme.js';
10
+ import { ALL_COLLECTORS } from './collectors/index.js';
11
+ import { createEmptyState } from './state.js';
12
+ import { evaluateStatuses } from './strategies/index.js';
13
+ import { collectDetails } from './collector.js';
14
+ import { postToHub, logTime } from './watcher-http.js';
15
+ import { setupFileWatchers } from './watcher-fs.js';
16
+ const LOCKFILE_NAME = '.logbook-active';
17
+ /**
18
+ * Build and push a full status snapshot to the hub.
19
+ */
20
+ async function pushStatus(cwd, port, projectName, state, reason, jiraBaseUrl, repositoryUrl, primaryColor, agentName) {
21
+ const statuses = evaluateStatuses(state);
22
+ const details = await collectDetails(cwd);
23
+ // Render previews: read entry files, strip frontmatter, render to HTML.
24
+ let renderedStory;
25
+ let renderedLog;
26
+ let renderedTicket;
27
+ if (state.hasActiveTicket && state.activeTicketSlug) {
28
+ const config = getConfig(cwd);
29
+ const entriesDir = getLogbookDirPath(config, cwd);
30
+ const paths = getEntryFilePaths(entriesDir, state.activeTicketSlug);
31
+ if (existsSync(paths.index)) {
32
+ try {
33
+ const raw = readFileSync(paths.index, 'utf8');
34
+ renderedStory = await mdToHtml(matter(raw).content);
35
+ }
36
+ catch { }
37
+ }
38
+ if (existsSync(paths.log)) {
39
+ try {
40
+ const raw = readFileSync(paths.log, 'utf8');
41
+ renderedLog = await transformLogToTimeline(raw);
42
+ }
43
+ catch { }
44
+ }
45
+ if (existsSync(paths.ticket)) {
46
+ try {
47
+ const raw = readFileSync(paths.ticket, 'utf8');
48
+ renderedTicket = await mdToHtml(raw);
49
+ }
50
+ catch { }
51
+ }
52
+ }
53
+ const status = {
54
+ projectPath: cwd,
55
+ projectName,
56
+ branch: state.branch,
57
+ activeTicket: state.activeTicketId ?? undefined,
58
+ latestLog: state.latestLog ?? undefined,
59
+ statuses,
60
+ lastActivity: state.lastActivityTimestamp,
61
+ details,
62
+ renderedStory,
63
+ renderedLog,
64
+ renderedTicket,
65
+ mrUrl: state.mrUrl,
66
+ jiraStatus: state.jiraStatus,
67
+ jiraBaseUrl,
68
+ repositoryUrl,
69
+ primaryColor,
70
+ agentName,
71
+ glabLastUpdated: state.glabLastUpdated,
72
+ jiraLastUpdated: state.jiraLastUpdated,
73
+ };
74
+ await postToHub(port, '/_logbook/status', status);
75
+ if (state.jiraAssignedIssues) {
76
+ await postToHub(port, '/_logbook/jira-issues', {
77
+ projectPath: cwd,
78
+ issues: state.jiraAssignedIssues,
79
+ });
80
+ }
81
+ if (state.glabOpenMrs) {
82
+ await postToHub(port, '/_logbook/gitlab-mrs', {
83
+ projectPath: cwd,
84
+ mrs: state.glabOpenMrs,
85
+ });
86
+ }
87
+ console.log(info(`[${logTime()}] ${reason}`));
88
+ }
89
+ /**
90
+ * Start the watcher loop for the given project.
91
+ */
92
+ export async function startWatcher(cwd, port) {
93
+ const config = getConfig(cwd);
94
+ const projectName = config.projectName;
95
+ const entriesDir = getLogbookDirPath(config, cwd);
96
+ const lockfilePath = join(cwd, LOCKFILE_NAME);
97
+ // --- Check availability once at startup ---
98
+ const activeCollectors = [];
99
+ for (const collector of ALL_COLLECTORS) {
100
+ const isAvailable = await collector.available(cwd);
101
+ if (isAvailable) {
102
+ activeCollectors.push(collector);
103
+ console.log(info(`[${logTime()}] Collector '${collector.name}' active`));
104
+ }
105
+ else {
106
+ console.log(info(`[${logTime()}] Collector '${collector.name}' skipped (not available)`));
107
+ }
108
+ }
109
+ // --- Initialize shared state ---
110
+ const state = createEmptyState();
111
+ for (const collector of activeCollectors) {
112
+ await collector.collect(cwd, state);
113
+ }
114
+ const payload = { projectPath: cwd, projectName };
115
+ await postToHub(port, '/_logbook/register', payload);
116
+ const agentName = undefined;
117
+ const pushCfg = {
118
+ jiraBaseUrl: config.jiraBaseUrl ?? '',
119
+ repositoryUrl: config.repositoryUrl,
120
+ primaryColor: config.primaryColor,
121
+ agentName,
122
+ };
123
+ const ps = (reason) => pushStatus(cwd, port, projectName, state, reason, pushCfg.jiraBaseUrl, pushCfg.repositoryUrl, pushCfg.primaryColor, pushCfg.agentName);
124
+ await ps('Initial status collected');
125
+ const runAndPush = async (collector, reason) => {
126
+ try {
127
+ const prevBranch = state.branch;
128
+ await collector.collect(cwd, state);
129
+ if (collector.name === 'git' && state.branch !== prevBranch) {
130
+ for (const c of activeCollectors) {
131
+ if (c.name === 'entries' || c.name === 'glab' || c.name === 'jira')
132
+ await c.collect(cwd, state);
133
+ }
134
+ }
135
+ await ps(reason);
136
+ }
137
+ catch { }
138
+ };
139
+ const fsHandles = setupFileWatchers({ cwd, port, entriesDir, lockfilePath, activeCollectors, state, pushStatus: ps });
140
+ // --- Interval timers for interval-triggered collectors ---
141
+ const intervals = [];
142
+ for (const collector of activeCollectors) {
143
+ const schedule = collector.schedule;
144
+ if (schedule.type !== 'interval')
145
+ continue;
146
+ const timer = setInterval(() => {
147
+ void runAndPush(collector, `${collector.name} collector (periodic)`);
148
+ }, schedule.ms);
149
+ intervals.push(timer);
150
+ }
151
+ // --- Graceful shutdown ---
152
+ const cleanup = async () => {
153
+ for (const timer of intervals)
154
+ clearInterval(timer);
155
+ fsHandles.clearDebounce();
156
+ for (const w of fsHandles.watchers)
157
+ w.close();
158
+ try {
159
+ await Promise.race([
160
+ postToHub(port, '/_logbook/deregister', { projectPath: cwd }),
161
+ new Promise((resolve) => setTimeout(resolve, 1000)),
162
+ ]);
163
+ }
164
+ catch {
165
+ // Hub already gone.
166
+ }
167
+ console.log(info(`[${logTime()}] Watcher stopped. Goodbye.`));
168
+ process.exit(0);
169
+ };
170
+ process.on('SIGINT', () => void cleanup());
171
+ process.on('SIGTERM', () => void cleanup());
172
+ console.log(success(`[${logTime()}] Watching with ${activeCollectors.length} collector(s): ${activeCollectors.map((c) => c.name).join(', ')}`));
173
+ }
174
+ //# sourceMappingURL=watcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"watcher.js","sourceRoot":"","sources":["../../src/watch/watcher.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "project-logbook",
3
- "version": "1.1.0",
3
+ "version": "1.3.0",
4
4
  "description": "A command-line tool for project logbooks.",
5
5
  "workspaces": [
6
6
  "demo-app"
@@ -20,6 +20,7 @@
20
20
  "scripts": {
21
21
  "dev": "tsx src/index.ts",
22
22
  "build": "rimraf dist && tsc && mkdir -p dist/templates && cp src/templates/*.* dist/templates/ && chmod +x dist/index.js",
23
+ "crawl": "node scripts/crawl.js",
23
24
  "prepublishOnly": "npm run build",
24
25
  "lint": "eslint src --ext .ts --fix",
25
26
  "format": "prettier --write --list-different .",
@@ -27,7 +28,7 @@
27
28
  "madge": "madge --circular --extensions ts --ts-config ./tsconfig.json src/index.ts",
28
29
  "jscpd": "jscpd src",
29
30
  "test": "vitest run",
30
- "pre": "npm run format && npm run lint && npm run test && npm run build && npm run knip && npm run madge && npm run jscpd && npm run dev build && npm run dev lint && npm audit",
31
+ "pre": "npm run format && npm run lint && npm run test && npm run build && npm run knip && npm run madge && npm run jscpd && npm run dev build && npm run dev lint && npm audit --audit-level=high",
31
32
  "next": "node scripts/create-ticket-from-backlog.js",
32
33
  "link": "npm run build && npm link",
33
34
  "open": "npm run dev build && node scripts/open-and-view-dist.js"
@@ -65,7 +65,17 @@ Once the work is ready:
65
65
 
66
66
  The linter enforces rules to be followed.
67
67
 
68
- ### 4. Final Review
68
+ ### 4. Interactive Local QA Loop
69
+ Development is an iterative process. Expect to go through several feedback loops with the prompter before a ticket is finalized:
70
+ - **Test & Challenge**: The prompter will run and test your changes, challenge architectural patterns, and suggest revisions.
71
+ - **Cycle Routine**: During each feedback round:
72
+ 1. Update code according to feedback.
73
+ 2. Log key decisions, errors, and pivots in `log.md` (`logbook log "..."`).
74
+ 3. Keep the story in `index.md` fully up-to-date with any design/code changes.
75
+ 4. Run `npm run pre` to verify linting and tests pass.
76
+ - **Commit Optimization**: Once the implementation is solid, optimize the final git commit message to match project standards before releasing.
77
+
78
+ ### 5. Final Review
69
79
  Use the preview command to see how your entry looks in the context of the whole project:
70
80
  ```bash
71
81
  logbook preview
@@ -88,9 +98,31 @@ If `jiraPrefix` is configured, entry folder names and ticket references must mat
88
98
 
89
99
  ## Research Logs
90
100
  For exploratory research, technical spike investigations, and architectural decision groundwork, we use **Research Logs**.
91
- - **Location**: Stored in `docs/research/` (configurable via `researchDir`).
92
- - **Scaffolding**: Run `logbook new [ID] [Slug] --research` or `logbook new -r` to scaffold. This automatically generates a numeric ID prefixed with `RES-` (e.g. `RES-1`).
101
+
102
+ - **Required Setup**: Before creating your first research log, ensure that your configured research directory exists on your filesystem (for example, create it using `mkdir -p docs/research`).
103
+ - **Location**: Stored in `docs/research/` (configurable via `researchDir` in `.project-logbook`).
104
+ - **Scaffolding**: Run `logbook new [ID] [Slug] --research` or `logbook new -r` to scaffold. This automatically generates a numeric ID prefixed with `RES-` (e.g., `RES-1`).
93
105
  - **Commands**: Commands like `logbook start RES-1`, `logbook log`, and `logbook release` work transparently.
94
106
  - **Auto-Activity Tracking**: Writing a log message via `logbook log` automatically updates the `updated` frontmatter field inside the research log's `index.md`.
95
107
  - **Status Lifecycle**: The frontmatter `status` field tracks progress: `active`, `paused`, `postponed`, or `completed`.
96
108
  - **Visuals**: Research logs are compiled and displayed under a dedicated "Research" tab on the built logbook homepage, sorted by their latest `updated` date.
109
+
110
+ ## Custom Storyline Types
111
+ Beyond standard logbook entries and Research Logs, you can define completely custom storyline types (e.g., "analyses", "spikes", "decisions") in `.project-logbook`.
112
+
113
+ - **Required Setup**: Define the custom types inside your `.project-logbook` configuration file under `customTypes` and ensure the directory exists on your filesystem:
114
+ ```json
115
+ {
116
+ "customTypes": {
117
+ "analyses": {
118
+ "prefix": "ANA",
119
+ "dir": "docs/analyses",
120
+ "tabTitle": "Analyses",
121
+ "sortBy": "updated"
122
+ }
123
+ }
124
+ }
125
+ ```
126
+ - **Directory Setup**: Ensure the directory configured (e.g., `docs/analyses`) is created on your filesystem (for example, create it using `mkdir -p docs/analyses`).
127
+ - **Scaffolding**: Run `logbook new [ID] [Slug] --type analyses` or `logbook new -t analyses` to scaffold the entry.
128
+ - **Visuals**: Just like Research Logs, these custom types will automatically compile into dedicated tabs on the built logbook homepage using your specified `tabTitle`.
@@ -0,0 +1,29 @@
1
+ # logbook Hub
2
+
3
+ The **logbook Hub** is a real-time dashboard for monitoring multiple project-logbook instances on your machine.
4
+
5
+ ## How it works
6
+
7
+ - The first `logbook watch` invocation binds **port 4201** and becomes the Hub server.
8
+ - Subsequent invocations detect the Hub, register as a Watcher, and push status updates.
9
+ - Each project appears as a card on the dashboard with status badges.
10
+ - The dashboard updates in real time via **Server-Sent Events (SSE)**.
11
+
12
+ ## What you see
13
+
14
+ | Element | Description |
15
+ |---------|-------------|
16
+ | **Badges** | Color-coded status indicators (green = good, yellow = warning, blue = info) |
17
+ | **Branch** | Current git branch, click to open in the repository |
18
+ | **Ticket** | Active logbook ticket, click to open in Jira (if configured) |
19
+ | **Activity** | Recent events: log entries, starts, releases |
20
+ | **Previews** | Story, Log, and Ticket content rendered as HTML (click buttons) |
21
+ | **Details** | LOC stats, staged/unstaged files, commit counts (click card to expand) |
22
+
23
+ ## Status badges
24
+
25
+ The Help tab lists every status strategy with its severity and which badges it blocks.
26
+
27
+ ## Jira & GitLab tabs
28
+
29
+ When a project has `jiraBaseUrl` configured and `JIRA_API_TOKEN` set, assigned issues appear in the Jira tab. When `glab` CLI is installed and authenticated, open merge requests appear in the GitLab tab.
@@ -0,0 +1,34 @@
1
+ ---
2
+ id: [ID_NUM]
3
+ title: [YAML_TITLE]
4
+ status: "proposed"
5
+ date: [DATE]
6
+ author: "[AUTHOR]"
7
+ superseded_by: null
8
+ tags: []
9
+ ---
10
+
11
+ # ADR [ID]: [TITLE]
12
+
13
+ ## Context
14
+ [Describe the issue motivating this decision and any context that influences it.]
15
+
16
+ ## Considered Options
17
+ 1. **[Option A]**: [Brief description.]
18
+ 2. **[Option B]**: [Brief description.]
19
+
20
+ ## Decision
21
+ [State the decision and briefly explain what it means in practice.]
22
+
23
+ ## Rationale
24
+ [Explain why this option was chosen over the alternatives.]
25
+
26
+ ## Consequences
27
+
28
+ ### Positive
29
+ * [Benefit 1]
30
+ * [Benefit 2]
31
+
32
+ ### Negative
33
+ * [Drawback 1]
34
+ * [Drawback 2]