project-logbook 0.4.1 → 1.1.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 (169) hide show
  1. package/README.md +38 -2
  2. package/dist/commands/build.js +75 -85
  3. package/dist/commands/build.js.map +1 -0
  4. package/dist/commands/init.js +1 -0
  5. package/dist/commands/init.js.map +1 -0
  6. package/dist/commands/lint.js +9 -3
  7. package/dist/commands/lint.js.map +1 -0
  8. package/dist/commands/list.js +14 -26
  9. package/dist/commands/list.js.map +1 -0
  10. package/dist/commands/log.d.ts +1 -3
  11. package/dist/commands/log.js +28 -4
  12. package/dist/commands/log.js.map +1 -0
  13. package/dist/commands/new.d.ts +4 -1
  14. package/dist/commands/new.js +73 -52
  15. package/dist/commands/new.js.map +1 -0
  16. package/dist/commands/preview.js +1 -0
  17. package/dist/commands/preview.js.map +1 -0
  18. package/dist/commands/release.js +2 -1
  19. package/dist/commands/release.js.map +1 -0
  20. package/dist/commands/start.d.ts +1 -1
  21. package/dist/commands/start.js +76 -16
  22. package/dist/commands/start.js.map +1 -0
  23. package/dist/commands/status.js +10 -18
  24. package/dist/commands/status.js.map +1 -0
  25. package/dist/commands/steer.js +1 -0
  26. package/dist/commands/steer.js.map +1 -0
  27. package/dist/commands/upgrade.js +1 -0
  28. package/dist/commands/upgrade.js.map +1 -0
  29. package/dist/index.js +7 -5
  30. package/dist/index.js.map +1 -0
  31. package/dist/lib/about-content.js +1 -0
  32. package/dist/lib/about-content.js.map +1 -0
  33. package/dist/lib/adr-renderer.d.ts +12 -0
  34. package/dist/lib/adr-renderer.js +53 -0
  35. package/dist/lib/adr-renderer.js.map +1 -0
  36. package/dist/lib/autolink.d.ts +15 -0
  37. package/dist/lib/autolink.js +39 -0
  38. package/dist/lib/autolink.js.map +1 -0
  39. package/dist/lib/build-helpers.d.ts +1 -1
  40. package/dist/lib/build-helpers.js +24 -7
  41. package/dist/lib/build-helpers.js.map +1 -0
  42. package/dist/lib/build-steps.d.ts +16 -2
  43. package/dist/lib/build-steps.js +124 -20
  44. package/dist/lib/build-steps.js.map +1 -0
  45. package/dist/lib/config.d.ts +22 -0
  46. package/dist/lib/config.js +29 -1
  47. package/dist/lib/config.js.map +1 -0
  48. package/dist/lib/custom-card-renderer.d.ts +2 -0
  49. package/dist/lib/custom-card-renderer.js +58 -0
  50. package/dist/lib/custom-card-renderer.js.map +1 -0
  51. package/dist/lib/custom-types-builder.d.ts +4 -0
  52. package/dist/lib/custom-types-builder.js +37 -0
  53. package/dist/lib/custom-types-builder.js.map +1 -0
  54. package/dist/lib/entry-id.js +1 -0
  55. package/dist/lib/entry-id.js.map +1 -0
  56. package/dist/lib/entry-paths.js +1 -0
  57. package/dist/lib/entry-paths.js.map +1 -0
  58. package/dist/lib/git-helpers.d.ts +5 -16
  59. package/dist/lib/git-helpers.js +61 -78
  60. package/dist/lib/git-helpers.js.map +1 -0
  61. package/dist/lib/git-url-helpers.d.ts +16 -0
  62. package/dist/lib/git-url-helpers.js +76 -0
  63. package/dist/lib/git-url-helpers.js.map +1 -0
  64. package/dist/lib/hast-helpers.js +1 -0
  65. package/dist/lib/hast-helpers.js.map +1 -0
  66. package/dist/lib/html-attributes.js +1 -0
  67. package/dist/lib/html-attributes.js.map +1 -0
  68. package/dist/lib/html-escape.js +1 -0
  69. package/dist/lib/html-escape.js.map +1 -0
  70. package/dist/lib/image-dimension-parser.d.ts +11 -0
  71. package/dist/lib/image-dimension-parser.js +127 -0
  72. package/dist/lib/image-dimension-parser.js.map +1 -0
  73. package/dist/lib/image-helpers.d.ts +2 -0
  74. package/dist/lib/image-helpers.js +22 -30
  75. package/dist/lib/image-helpers.js.map +1 -0
  76. package/dist/lib/jira-helpers.js +1 -0
  77. package/dist/lib/jira-helpers.js.map +1 -0
  78. package/dist/lib/lint-runner.js +1 -0
  79. package/dist/lib/lint-runner.js.map +1 -0
  80. package/dist/lib/lint-types.js +1 -0
  81. package/dist/lib/lint-types.js.map +1 -0
  82. package/dist/lib/lockfile.js +1 -0
  83. package/dist/lib/lockfile.js.map +1 -0
  84. package/dist/lib/logbook-client.js +1 -0
  85. package/dist/lib/logbook-client.js.map +1 -0
  86. package/dist/lib/markdown-processors.js +1 -0
  87. package/dist/lib/markdown-processors.js.map +1 -0
  88. package/dist/lib/migrations.js +1 -0
  89. package/dist/lib/migrations.js.map +1 -0
  90. package/dist/lib/package-version.js +1 -0
  91. package/dist/lib/package-version.js.map +1 -0
  92. package/dist/lib/post-template.d.ts +5 -0
  93. package/dist/lib/post-template.js +48 -0
  94. package/dist/lib/post-template.js.map +1 -0
  95. package/dist/lib/rss.js +1 -0
  96. package/dist/lib/rss.js.map +1 -0
  97. package/dist/lib/session.d.ts +2 -2
  98. package/dist/lib/session.js +75 -35
  99. package/dist/lib/session.js.map +1 -0
  100. package/dist/lib/styles.js +1 -0
  101. package/dist/lib/styles.js.map +1 -0
  102. package/dist/lib/template-helpers.d.ts +6 -1
  103. package/dist/lib/template-helpers.js +81 -1
  104. package/dist/lib/template-helpers.js.map +1 -0
  105. package/dist/lib/template-types.d.ts +37 -5
  106. package/dist/lib/template-types.js +1 -0
  107. package/dist/lib/template-types.js.map +1 -0
  108. package/dist/lib/templates.d.ts +11 -8
  109. package/dist/lib/templates.js +59 -60
  110. package/dist/lib/templates.js.map +1 -0
  111. package/dist/lib/theme.js +1 -0
  112. package/dist/lib/theme.js.map +1 -0
  113. package/dist/lib/url-helpers.js +1 -0
  114. package/dist/lib/url-helpers.js.map +1 -0
  115. package/dist/linters/diff-to-narrative.d.ts +2 -1
  116. package/dist/linters/diff-to-narrative.js +3 -10
  117. package/dist/linters/diff-to-narrative.js.map +1 -0
  118. package/dist/linters/frontmatter.js +1 -0
  119. package/dist/linters/frontmatter.js.map +1 -0
  120. package/dist/linters/index.js +1 -0
  121. package/dist/linters/index.js.map +1 -0
  122. package/dist/linters/jira-prefix.js +1 -0
  123. package/dist/linters/jira-prefix.js.map +1 -0
  124. package/dist/linters/links.js +26 -4
  125. package/dist/linters/links.js.map +1 -0
  126. package/dist/linters/lockfile.js +1 -0
  127. package/dist/linters/lockfile.js.map +1 -0
  128. package/dist/linters/placeholders.js +6 -4
  129. package/dist/linters/placeholders.js.map +1 -0
  130. package/dist/linters/project-integrity.js +1 -0
  131. package/dist/linters/project-integrity.js.map +1 -0
  132. package/dist/linters/readability.js +2 -3
  133. package/dist/linters/readability.js.map +1 -0
  134. package/dist/linters/technical-log.js +1 -0
  135. package/dist/linters/technical-log.js.map +1 -0
  136. package/dist/linters/workspaces.js +1 -0
  137. package/dist/linters/workspaces.js.map +1 -0
  138. package/dist/templates/CONTRIBUTING.md +10 -1
  139. package/dist/templates/index.md +2 -1
  140. package/dist/templates/log.md +1 -1
  141. package/dist/templates/styles.css +171 -32
  142. package/dist/utils/date.js +1 -0
  143. package/dist/utils/date.js.map +1 -0
  144. package/dist/utils/frontmatter.d.ts +9 -0
  145. package/dist/utils/frontmatter.js +16 -0
  146. package/dist/utils/frontmatter.js.map +1 -0
  147. package/dist/utils/fs.d.ts +10 -0
  148. package/dist/utils/fs.js +112 -0
  149. package/dist/utils/fs.js.map +1 -0
  150. package/dist/utils/id.js +1 -0
  151. package/dist/utils/id.js.map +1 -0
  152. package/dist/utils/log-timeline.d.ts +2 -1
  153. package/dist/utils/log-timeline.js +1 -0
  154. package/dist/utils/log-timeline.js.map +1 -0
  155. package/dist/utils/slug.js +1 -0
  156. package/dist/utils/slug.js.map +1 -0
  157. package/dist/utils/string.js +1 -0
  158. package/dist/utils/string.js.map +1 -0
  159. package/dist/utils/timeline-helpers.d.ts +2 -2
  160. package/dist/utils/timeline-helpers.js +5 -2
  161. package/dist/utils/timeline-helpers.js.map +1 -0
  162. package/dist/utils/validation.d.ts +5 -0
  163. package/dist/utils/validation.js +21 -0
  164. package/dist/utils/validation.js.map +1 -0
  165. package/package.json +12 -6
  166. package/src/templates/CONTRIBUTING.md +10 -1
  167. package/src/templates/index.md +2 -1
  168. package/src/templates/log.md +1 -1
  169. package/src/templates/styles.css +171 -32
@@ -0,0 +1,15 @@
1
+ export declare function registerResearchEntries(entries: {
2
+ ticket?: string;
3
+ slug: string;
4
+ customTypeName?: string;
5
+ }[]): void;
6
+ export declare function getRegisteredResearchEntries(): {
7
+ ticket?: string;
8
+ slug: string;
9
+ customTypeName?: string;
10
+ }[];
11
+ export declare function autolinkReferences(html: string, researchEntries: {
12
+ ticket?: string;
13
+ slug: string;
14
+ customTypeName?: string;
15
+ }[], basePath: string): string;
@@ -0,0 +1,39 @@
1
+ // ponytail: global registry of research/custom entries to avoid passing it down deep callstacks
2
+ let registeredResearchEntries = [];
3
+ export function registerResearchEntries(entries) {
4
+ registeredResearchEntries = entries;
5
+ }
6
+ export function getRegisteredResearchEntries() {
7
+ return registeredResearchEntries;
8
+ }
9
+ export function autolinkReferences(html, researchEntries, basePath) {
10
+ if (!researchEntries || researchEntries.length === 0)
11
+ return html;
12
+ const map = new Map(researchEntries.filter((e) => typeof e.ticket === 'string').map((e) => [e.ticket.toUpperCase(), e]));
13
+ const parts = html.split(/(<[^>]+>)/g);
14
+ let inAnchor = 0;
15
+ for (let i = 0; i < parts.length; i++) {
16
+ const part = parts[i];
17
+ if (part.startsWith('<')) {
18
+ if (/^<a\b/i.test(part)) {
19
+ inAnchor++;
20
+ }
21
+ else if (/^<\/a>/i.test(part)) {
22
+ inAnchor = Math.max(0, inAnchor - 1);
23
+ }
24
+ }
25
+ else if (inAnchor === 0) {
26
+ parts[i] = part.replace(/\b([A-Z]+-\d+)\b/gi, (match) => {
27
+ const key = match.toUpperCase();
28
+ const entry = map.get(key);
29
+ if (entry) {
30
+ const folder = entry.customTypeName ? `${entry.customTypeName}/` : 'research/';
31
+ return `<a href="${basePath}${folder}${entry.slug}/index.html" class="research-link">${match}</a>`;
32
+ }
33
+ return match;
34
+ });
35
+ }
36
+ }
37
+ return parts.join('');
38
+ }
39
+ //# sourceMappingURL=autolink.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autolink.js","sourceRoot":"","sources":["../../src/lib/autolink.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,IAAI,yBAAyB,GAAiE,EAAE,CAAC;AAEjG,MAAM,UAAU,uBAAuB,CAAC,OAAqE;IAC3G,yBAAyB,GAAG,OAAO,CAAC;AACtC,CAAC;AAED,MAAM,UAAU,4BAA4B;IAC1C,OAAO,yBAAyB,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,IAAY,EACZ,eAA6E,EAC7E,QAAgB;IAEhB,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAClE,MAAM,GAAG,GAAG,IAAI,GAAG,CACjB,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,MAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,CAAC,CACrG,CAAC;IACF,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;IACvC,IAAI,QAAQ,GAAG,CAAC,CAAC;IAEjB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;QACtB,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACzB,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACxB,QAAQ,EAAE,CAAC;YACb,CAAC;iBAAM,IAAI,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChC,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,GAAG,CAAC,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;aAAM,IAAI,QAAQ,KAAK,CAAC,EAAE,CAAC;YAC1B,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,EAAE,CAAC,KAAK,EAAE,EAAE;gBACtD,MAAM,GAAG,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;gBAChC,MAAM,KAAK,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;gBAC3B,IAAI,KAAK,EAAE,CAAC;oBACV,MAAM,MAAM,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,cAAc,GAAG,CAAC,CAAC,CAAC,WAAW,CAAC;oBAC/E,OAAO,YAAY,QAAQ,GAAG,MAAM,GAAG,KAAK,CAAC,IAAI,sCAAsC,KAAK,MAAM,CAAC;gBACrG,CAAC;gBACD,OAAO,KAAK,CAAC;YACf,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;AACxB,CAAC"}
@@ -14,7 +14,7 @@ export declare function renderPost(data: TimelineEntry, i: number, allEntries: T
14
14
  config: LogbookConfig;
15
15
  version: string;
16
16
  buildTime: string;
17
- }): Promise<void>;
17
+ }, isResearch?: boolean, customTypeName?: string): Promise<void>;
18
18
  /**
19
19
  * Generate the standard footer metadata string.
20
20
  * Always hardcoded to 'Project Logbook CLI' with link to npm.
@@ -1,5 +1,5 @@
1
1
  import fs from 'fs-extra';
2
- import { join, resolve } from 'node:path';
2
+ import { join } from 'node:path';
3
3
  import matter from 'gray-matter';
4
4
  import { layout, postTemplate } from './templates.js';
5
5
  import { getGitCommits } from './git-helpers.js';
@@ -70,8 +70,8 @@ async function renderLinkedMdFiles(markdownSources, entryPath, entryOutputDir, c
70
70
  continue;
71
71
  }
72
72
  // Path traversal protection: ensure resolved path stays within project boundaries
73
- const resolvedPath = resolve(sourcePath);
74
- const projectRoot = resolve(process.cwd());
73
+ const resolvedPath = fs.realpathSync(sourcePath);
74
+ const projectRoot = fs.realpathSync(process.cwd());
75
75
  if (!resolvedPath.startsWith(projectRoot + '/') && resolvedPath !== projectRoot) {
76
76
  console.warn(` Path traversal blocked: ${href}`);
77
77
  continue;
@@ -95,8 +95,13 @@ async function renderLinkedMdFiles(markdownSources, entryPath, entryOutputDir, c
95
95
  }
96
96
  }
97
97
  }
98
- export async function renderPost(data, i, allEntries, ctx) {
99
- const entryFilePaths = getEntryFilePaths(ctx.logbookDir, data.slug);
98
+ export async function renderPost(data, i, allEntries, ctx, isResearch = false, customTypeName) {
99
+ const currentLogbookDir = customTypeName
100
+ ? ctx.config.customTypes?.[customTypeName]?.dir || ''
101
+ : isResearch
102
+ ? ctx.config.researchDir || 'docs/research'
103
+ : ctx.logbookDir;
104
+ const entryFilePaths = getEntryFilePaths(currentLogbookDir, data.slug);
100
105
  const entryPath = entryFilePaths.index.replace(/\/index\.md$/, '');
101
106
  const { content, content: summaryContent } = matter(await fs.readFile(entryFilePaths.index, 'utf8'));
102
107
  const ticketRaw = await readFileIfExists(entryFilePaths.ticket);
@@ -113,6 +118,8 @@ export async function renderPost(data, i, allEntries, ctx) {
113
118
  const next = i < allEntries.length - 1 ? allEntries[i + 1] : null;
114
119
  // Transform log to timeline if possible, otherwise fall back to plain markdown
115
120
  const logHtml = logRaw ? await transformLogToTimeline(logRaw) : '';
121
+ const backLink = isResearch || customTypeName ? '../../index.html' : '../index.html';
122
+ const basePath = isResearch || customTypeName ? '../../' : '../';
116
123
  const { header: postHeader, content: postMainContent } = postTemplate({
117
124
  ...data,
118
125
  title: data.title ?? data.slug,
@@ -126,20 +133,29 @@ export async function renderPost(data, i, allEntries, ctx) {
126
133
  repositoryUrl: ctx.config.repositoryUrl,
127
134
  prevEntry: prev ? { slug: prev.slug, title: prev.title ?? '', ticket: prev.ticket } : null,
128
135
  nextEntry: next ? { slug: next.slug, title: next.title ?? '', ticket: next.ticket } : null,
136
+ backLink,
129
137
  });
130
138
  const descriptionRaw = typeof data.summary === 'string' ? data.summary.replace(/[*#`]/g, '').trim() : '';
131
139
  const buildMeta = generateBuildMeta(ctx.version, ctx.buildTime);
140
+ const prevUrl = prev ? `../${prev.slug}/index.html` : undefined;
141
+ const nextUrl = next ? `../${next.slug}/index.html` : undefined;
132
142
  const postHtml = layout({
133
143
  title: data.title ?? data.slug,
134
144
  projectName: ctx.config.projectName,
135
- basePath: '../',
145
+ basePath,
136
146
  description: descriptionRaw.substring(0, 160),
137
147
  bodySlug: data.slug,
138
148
  buildMeta,
139
149
  header: postHeader,
140
150
  content: postMainContent,
151
+ prevUrl,
152
+ nextUrl,
141
153
  });
142
- const entryOutputDir = join(ctx.outputDir, data.slug);
154
+ const entryOutputDir = customTypeName
155
+ ? join(ctx.outputDir, customTypeName, data.slug)
156
+ : isResearch
157
+ ? join(ctx.outputDir, 'research', data.slug)
158
+ : join(ctx.outputDir, data.slug);
143
159
  await fs.mkdirp(entryOutputDir);
144
160
  await fs.writeFile(join(entryOutputDir, 'index.html'), postHtml);
145
161
  // Copy images to output directory
@@ -156,3 +172,4 @@ export async function renderPost(data, i, allEntries, ctx) {
156
172
  export function generateBuildMeta(version, buildTime) {
157
173
  return `Generated by <a href="https://www.npmjs.com/package/project-logbook" target="_blank" rel="noopener noreferrer">Project Logbook CLI</a> v${version} • ${buildTime}`;
158
174
  }
175
+ //# sourceMappingURL=build-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-helpers.js","sourceRoot":"","sources":["../../src/lib/build-helpers.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,6 @@
1
1
  import type { LogbookConfig } from './config.js';
2
+ import type { TimelineEntry } from './template-types.js';
3
+ import type { EntryMeta } from '../utils/fs.js';
2
4
  /**
3
5
  * Set up the output directory: create it, copy static assets (favicon, styles, script).
4
6
  */
@@ -13,12 +15,24 @@ export declare function processReadme(projectRoot: string): Promise<{
13
15
  /**
14
16
  * Copy README images to the output directory.
15
17
  */
16
- export declare function copyReadmeImages(projectRoot: string, outputDir: string, imagePaths: string[]): Promise<void>;
18
+ export declare const copyReadmeImages: (projectRoot: string, outputDir: string, imagePaths: string[]) => Promise<void>;
17
19
  /**
18
20
  * Process the About content and return rendered HTML.
19
21
  */
20
- export declare function processAboutContent(aboutContent: string): Promise<string>;
22
+ export declare const processAboutContent: (aboutContent: string) => Promise<string>;
21
23
  /**
22
24
  * Process the Welcome content and return rendered HTML.
23
25
  */
24
26
  export declare function processWelcomeContent(): Promise<string>;
27
+ /**
28
+ * Generate and write RSS feed to the output directory.
29
+ */
30
+ export declare function buildRssFeed(outputDir: string, timelineEntries: TimelineEntry[], config: LogbookConfig, version: string): Promise<void>;
31
+ /**
32
+ * Processes raw logbook entry metadata into chronological timeline entries.
33
+ */
34
+ export declare function processTimelineEntries(logbookEntries: EntryMeta[], activeEntry: {
35
+ slug: string;
36
+ } | null | undefined, availableWorkspaces: string[]): Promise<(TimelineEntry & {
37
+ monthGroup: string;
38
+ })[]>;
@@ -3,22 +3,28 @@
3
3
  * Breaks down the monolithic build() function into testable, reusable steps.
4
4
  */
5
5
  import fs from 'fs-extra';
6
- import { join } from 'node:path';
7
- import { warning } from './theme.js';
6
+ import { join, relative } from 'node:path';
7
+ import { warning, error, neutral } from './theme.js';
8
8
  import { URL, fileURLToPath } from 'node:url';
9
- import { mdToHtmlWithImages, copyImages } from './image-helpers.js';
9
+ import { mdToHtmlWithImages, copyImages, extractImagePathsFromMarkdown, getImageDimensions } from './image-helpers.js';
10
10
  import { getStyles } from './styles.js';
11
11
  import { getClientScript } from './logbook-client.js';
12
12
  import { mdToHtml } from './build-helpers.js';
13
+ import { generateRssFeed, validateRssXml } from './rss.js';
14
+ import { asString, asStringArray, toDisplayString } from '../utils/frontmatter.js';
15
+ import { formatAbsoluteDate, getMonthYear, getSortTime } from '../utils/date.js';
16
+ import { getEntryDisplayId, getEntryDisplayTitle } from './entry-id.js';
13
17
  /**
14
18
  * Set up the output directory: create it, copy static assets (favicon, styles, script).
15
19
  */
16
20
  export async function setupOutputDirectory(outputDir, config) {
17
21
  await fs.remove(outputDir);
18
22
  await fs.mkdirp(outputDir);
19
- await fs.copyFile(new URL('../templates/favicon.svg', import.meta.url), join(outputDir, 'favicon.svg'));
20
- await fs.writeFile(join(outputDir, 'style.css'), getStyles(config.primaryColor));
21
- await fs.writeFile(join(outputDir, 'logbook.js'), getClientScript());
23
+ await Promise.all([
24
+ fs.copyFile(new URL('../templates/favicon.svg', import.meta.url), join(outputDir, 'favicon.svg')),
25
+ fs.writeFile(join(outputDir, 'style.css'), getStyles(config.primaryColor)),
26
+ fs.writeFile(join(outputDir, 'logbook.js'), getClientScript()),
27
+ ]);
22
28
  }
23
29
  /**
24
30
  * Process README.md if it exists and return rendered HTML + image paths.
@@ -29,8 +35,7 @@ export async function processReadme(projectRoot) {
29
35
  let readmeImages = [];
30
36
  try {
31
37
  if (await fs.pathExists(readmePath)) {
32
- const readmeContent = await fs.readFile(readmePath, 'utf8');
33
- const { html, imagePaths } = await mdToHtmlWithImages(readmeContent);
38
+ const { html, imagePaths } = await mdToHtmlWithImages(await fs.readFile(readmePath, 'utf8'));
34
39
  readmeHtml = html;
35
40
  readmeImages = imagePaths;
36
41
  }
@@ -44,27 +49,19 @@ export async function processReadme(projectRoot) {
44
49
  /**
45
50
  * Copy README images to the output directory.
46
51
  */
47
- export async function copyReadmeImages(projectRoot, outputDir, imagePaths) {
48
- if (imagePaths.length > 0) {
49
- await copyImages(projectRoot, outputDir, imagePaths);
50
- }
51
- }
52
+ export const copyReadmeImages = (projectRoot, outputDir, imagePaths) => imagePaths.length > 0 ? copyImages(projectRoot, outputDir, imagePaths) : Promise.resolve();
52
53
  /**
53
54
  * Process the About content and return rendered HTML.
54
55
  */
55
- export async function processAboutContent(aboutContent) {
56
- return await mdToHtml(aboutContent);
57
- }
56
+ export const processAboutContent = (aboutContent) => mdToHtml(aboutContent);
58
57
  /**
59
58
  * Process the Welcome content and return rendered HTML.
60
59
  */
61
60
  export async function processWelcomeContent() {
62
61
  try {
63
- const welcomeUrl = new URL('../templates/WELCOME.md', import.meta.url);
64
- const welcomePath = fileURLToPath(welcomeUrl);
62
+ const welcomePath = fileURLToPath(new URL('../templates/WELCOME.md', import.meta.url));
65
63
  if (await fs.pathExists(welcomePath)) {
66
- const welcomeContent = await fs.readFile(welcomePath, 'utf8');
67
- return await mdToHtml(welcomeContent);
64
+ return await mdToHtml(await fs.readFile(welcomePath, 'utf8'));
68
65
  }
69
66
  }
70
67
  catch (error) {
@@ -72,3 +69,110 @@ export async function processWelcomeContent() {
72
69
  }
73
70
  return '';
74
71
  }
72
+ /**
73
+ * Generate and write RSS feed to the output directory.
74
+ */
75
+ export async function buildRssFeed(outputDir, timelineEntries, config, version) {
76
+ const rssConfig = {
77
+ title: `${config.projectName} - Recent Updates`,
78
+ description: `Latest logbook entries from ${config.projectName}`,
79
+ language: 'en',
80
+ feed_url: `${config.projectName.toLowerCase().replace(/\s+/g, '-')}/rss.xml`,
81
+ site_url: config.projectName,
82
+ generator: `Project Logbook CLI v${version}`,
83
+ };
84
+ const rssXml = generateRssFeed(timelineEntries, rssConfig);
85
+ // Basic validation
86
+ if (!validateRssXml(rssXml)) {
87
+ console.warn(error('Warning: Generated RSS feed failed basic validation. Skipping rss.xml generation.'));
88
+ }
89
+ else {
90
+ await fs.writeFile(join(outputDir, 'rss.xml'), rssXml);
91
+ console.log(neutral(` Generated RSS feed with ${Math.min(50, timelineEntries.length)} entries`));
92
+ }
93
+ }
94
+ /**
95
+ * Processes raw logbook entry metadata into chronological timeline entries.
96
+ */
97
+ export async function processTimelineEntries(logbookEntries, activeEntry, availableWorkspaces) {
98
+ const timelineEntries = [];
99
+ for (const entry of logbookEntries) {
100
+ if (!entry.hasIndex)
101
+ continue;
102
+ const { data, content, slug } = entry;
103
+ let summary = data.summary;
104
+ if (!summary) {
105
+ const firstParagraph = content.split('\n\n').find((p) => p.trim() && !p.trim().startsWith('#'));
106
+ summary = firstParagraph
107
+ ? firstParagraph.trim().substring(0, 200).replace(/[*#`]/g, '') + '...'
108
+ : 'No summary available.';
109
+ }
110
+ const entryWorkspaces = asStringArray(data.workspaces).filter((ws) => availableWorkspaces.includes(ws));
111
+ const dateStartValue = data.dateStart;
112
+ const dateStart = typeof dateStartValue === 'string'
113
+ ? dateStartValue
114
+ : dateStartValue instanceof Date
115
+ ? dateStartValue.toISOString()
116
+ : '';
117
+ // Skip DRAFT entries (no valid dateStart) — they break the timeline
118
+ if (!dateStart || dateStart.includes('{{') || dateStart === '[DATE_START]') {
119
+ console.log(warning(`[DRAFT SKIPPED] Entry '${slug}' was excluded from the built site (missing valid dateStart).`));
120
+ if (activeEntry && activeEntry.slug === slug) {
121
+ console.log(warning(` → Your active session resolves to this entry. Run 'logbook start ${slug.split('-')[0]}' to initialize dateStart and make it visible.`));
122
+ }
123
+ continue;
124
+ }
125
+ const dateEndValue = data.dateEnd;
126
+ const dateEnd = typeof dateEndValue === 'string' && dateEndValue !== '[DATE_END]'
127
+ ? dateEndValue
128
+ : dateEndValue instanceof Date
129
+ ? dateEndValue.toISOString()
130
+ : undefined;
131
+ // Extract first image from index.md to use as a teaser image
132
+ let teaserImage;
133
+ const imagePaths = extractImagePathsFromMarkdown(content);
134
+ if (imagePaths.length > 0) {
135
+ const firstImage = imagePaths[0];
136
+ if (!firstImage.startsWith('http://') &&
137
+ !firstImage.startsWith('https://') &&
138
+ !firstImage.startsWith('/') &&
139
+ !firstImage.startsWith('data:')) {
140
+ try {
141
+ const absoluteImagePath = join(entry.path, firstImage);
142
+ const dims = await getImageDimensions(absoluteImagePath);
143
+ if (dims) {
144
+ const ratio = dims.width / dims.height;
145
+ const imageName = relative(entry.path, absoluteImagePath).replace(/\\/g, '/');
146
+ teaserImage = {
147
+ src: `./${slug}/images/${imageName}`,
148
+ ratio,
149
+ };
150
+ }
151
+ }
152
+ catch {
153
+ // On error, skip image
154
+ }
155
+ }
156
+ }
157
+ timelineEntries.push({
158
+ kind: 'entry',
159
+ slug,
160
+ dateStart,
161
+ dateEnd,
162
+ displayDate: formatAbsoluteDate(dateStart),
163
+ sortTime: getSortTime(dateStart, dateEnd),
164
+ summary: asString(summary, 'No summary available.') || '',
165
+ ticket: getEntryDisplayId(asString(data.ticket), slug),
166
+ monthGroup: getMonthYear(dateStart),
167
+ tags: asStringArray(data.tags),
168
+ workspaces: entryWorkspaces,
169
+ title: getEntryDisplayTitle(asString(data.title), slug),
170
+ harness: toDisplayString(data.harness),
171
+ llm: toDisplayString(data.llm),
172
+ prompter: toDisplayString(data.prompter),
173
+ teaserImage,
174
+ });
175
+ }
176
+ return timelineEntries;
177
+ }
178
+ //# sourceMappingURL=build-steps.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-steps.js","sourceRoot":"","sources":["../../src/lib/build-steps.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,EAAE,GAAG,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAE9C,OAAO,EAAE,kBAAkB,EAAE,UAAU,EAAE,6BAA6B,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AACvH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAE3D,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AACnF,OAAO,EAAE,kBAAkB,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAGxE;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,SAAiB,EAAE,MAAqB;IACjF,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3B,MAAM,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IAC3B,MAAM,OAAO,CAAC,GAAG,CAAC;QAChB,EAAE,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,0BAA0B,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,SAAS,EAAE,aAAa,CAAC,CAAC;QACjG,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,EAAE,SAAS,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC;QAC1E,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,EAAE,eAAe,EAAE,CAAC;KAC/D,CAAC,CAAC;AACL,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,WAAmB;IAIrD,MAAM,UAAU,GAAG,IAAI,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;IAClD,IAAI,UAAU,GAAG,EAAE,CAAC;IACpB,IAAI,YAAY,GAAa,EAAE,CAAC;IAEhC,IAAI,CAAC;QACH,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YACpC,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,MAAM,kBAAkB,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,CAAC;YAC7F,UAAU,GAAG,IAAI,CAAC;YAClB,YAAY,GAAG,UAAU,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,sCAAsC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;QAC9G,UAAU,GAAG,4BAA4B,CAAC;IAC5C,CAAC;IAED,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,WAAmB,EAAE,SAAiB,EAAE,UAAoB,EAAiB,EAAE,CAC9G,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;AAE7F;;GAEG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,YAAoB,EAAmB,EAAE,CAAC,QAAQ,CAAC,YAAY,CAAC,CAAC;AAErG;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,qBAAqB;IACzC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,aAAa,CAAC,IAAI,GAAG,CAAC,yBAAyB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;QACvF,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,CAAC;YACrC,OAAO,MAAM,QAAQ,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC,CAAC;QAChE,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,uCAAuC,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IACjH,CAAC;IACD,OAAO,EAAE,CAAC;AACZ,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,SAAiB,EACjB,eAAgC,EAChC,MAAqB,EACrB,OAAe;IAEf,MAAM,SAAS,GAAG;QAChB,KAAK,EAAE,GAAG,MAAM,CAAC,WAAW,mBAAmB;QAC/C,WAAW,EAAE,+BAA+B,MAAM,CAAC,WAAW,EAAE;QAChE,QAAQ,EAAE,IAAI;QACd,QAAQ,EAAE,GAAG,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,UAAU;QAC5E,QAAQ,EAAE,MAAM,CAAC,WAAW;QAC5B,SAAS,EAAE,wBAAwB,OAAO,EAAE;KAC7C,CAAC;IAEF,MAAM,MAAM,GAAG,eAAe,CAAC,eAAe,EAAE,SAAS,CAAC,CAAC;IAE3D,mBAAmB;IACnB,IAAI,CAAC,cAAc,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,mFAAmF,CAAC,CAAC,CAAC;IAC3G,CAAC;SAAM,CAAC;QACN,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,6BAA6B,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,eAAe,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC;IACpG,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,sBAAsB,CAC1C,cAA2B,EAC3B,WAAgD,EAChD,mBAA6B;IAE7B,MAAM,eAAe,GAA+C,EAAE,CAAC;IAEvE,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,CAAC;QACnC,IAAI,CAAC,KAAK,CAAC,QAAQ;YAAE,SAAS;QAE9B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;QACtC,IAAI,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC;QAC3B,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,cAAc,GAAG,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC;YAChG,OAAO,GAAG,cAAc;gBACtB,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,GAAG,KAAK;gBACvE,CAAC,CAAC,uBAAuB,CAAC;QAC9B,CAAC;QAED,MAAM,eAAe,GAAa,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;QAElH,MAAM,cAAc,GAAG,IAAI,CAAC,SAAS,CAAC;QACtC,MAAM,SAAS,GACb,OAAO,cAAc,KAAK,QAAQ;YAChC,CAAC,CAAC,cAAc;YAChB,CAAC,CAAC,cAAc,YAAY,IAAI;gBAC9B,CAAC,CAAC,cAAc,CAAC,WAAW,EAAE;gBAC9B,CAAC,CAAC,EAAE,CAAC;QAEX,oEAAoE;QACpE,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,SAAS,KAAK,cAAc,EAAE,CAAC;YAC3E,OAAO,CAAC,GAAG,CACT,OAAO,CAAC,0BAA0B,IAAI,+DAA+D,CAAC,CACvG,CAAC;YACF,IAAI,WAAW,IAAI,WAAW,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;gBAC7C,OAAO,CAAC,GAAG,CACT,OAAO,CACL,sEAAsE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,gDAAgD,CACzI,CACF,CAAC;YACJ,CAAC;YACD,SAAS;QACX,CAAC;QAED,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC;QAClC,MAAM,OAAO,GACX,OAAO,YAAY,KAAK,QAAQ,IAAI,YAAY,KAAK,YAAY;YAC/D,CAAC,CAAC,YAAY;YACd,CAAC,CAAC,YAAY,YAAY,IAAI;gBAC5B,CAAC,CAAC,YAAY,CAAC,WAAW,EAAE;gBAC5B,CAAC,CAAC,SAAS,CAAC;QAElB,6DAA6D;QAC7D,IAAI,WAAuD,CAAC;QAC5D,MAAM,UAAU,GAAG,6BAA6B,CAAC,OAAO,CAAC,CAAC;QAC1D,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC1B,MAAM,UAAU,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;YACjC,IACE,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC;gBACjC,CAAC,UAAU,CAAC,UAAU,CAAC,UAAU,CAAC;gBAClC,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,CAAC;gBAC3B,CAAC,UAAU,CAAC,UAAU,CAAC,OAAO,CAAC,EAC/B,CAAC;gBACD,IAAI,CAAC;oBACH,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;oBACvD,MAAM,IAAI,GAAG,MAAM,kBAAkB,CAAC,iBAAiB,CAAC,CAAC;oBACzD,IAAI,IAAI,EAAE,CAAC;wBACT,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC;wBACvC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,iBAAiB,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;wBAC9E,WAAW,GAAG;4BACZ,GAAG,EAAE,KAAK,IAAI,WAAW,SAAS,EAAE;4BACpC,KAAK;yBACN,CAAC;oBACJ,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,uBAAuB;gBACzB,CAAC;YACH,CAAC;QACH,CAAC;QAED,eAAe,CAAC,IAAI,CAAC;YACnB,IAAI,EAAE,OAAO;YACb,IAAI;YACJ,SAAS;YACT,OAAO;YACP,WAAW,EAAE,kBAAkB,CAAC,SAAS,CAAC;YAC1C,QAAQ,EAAE,WAAW,CAAC,SAAS,EAAE,OAAO,CAAC;YACzC,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,uBAAuB,CAAC,IAAI,EAAE;YACzD,MAAM,EAAE,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,EAAE,IAAI,CAAC;YACtD,UAAU,EAAE,YAAY,CAAC,SAAS,CAAC;YACnC,IAAI,EAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC;YAC9B,UAAU,EAAE,eAAe;YAC3B,KAAK,EAAE,oBAAoB,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,IAAI,CAAC;YACvD,OAAO,EAAE,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC;YACtC,GAAG,EAAE,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC;YAC9B,QAAQ,EAAE,eAAe,CAAC,IAAI,CAAC,QAAQ,CAAC;YACxC,WAAW;SACZ,CAAC,CAAC;IACL,CAAC;IAED,OAAO,eAAe,CAAC;AACzB,CAAC"}
@@ -10,6 +10,14 @@ export interface LogbookConfig {
10
10
  tags?: {
11
11
  allowed: string[];
12
12
  };
13
+ adrDir?: string;
14
+ researchDir?: string;
15
+ customTypes?: Record<string, {
16
+ prefix: string;
17
+ dir: string;
18
+ tabTitle: string;
19
+ sortBy?: 'dateStart' | 'updated';
20
+ }>;
13
21
  }
14
22
  export declare const DEFAULT_CONFIG: LogbookConfig;
15
23
  export declare function getConfig(cwd?: string): LogbookConfig;
@@ -29,3 +37,17 @@ export declare function getLogbookDirPath(config: LogbookConfig, cwd?: string):
29
37
  * @returns Absolute path to the output directory
30
38
  */
31
39
  export declare function getOutputDirPath(config: LogbookConfig, cwd?: string): string;
40
+ /**
41
+ * Get the absolute path to the ADR directory.
42
+ * @param config - The logbook config
43
+ * @param cwd - Optional working directory (defaults to process.cwd())
44
+ * @returns Absolute path to the ADR directory or null if not configured
45
+ */
46
+ export declare function getAdrDirPath(config: LogbookConfig, cwd?: string): string | null;
47
+ /**
48
+ * Get the absolute path to the Research directory.
49
+ * @param config - The logbook config
50
+ * @param cwd - Optional working directory (defaults to process.cwd())
51
+ * @returns Absolute path to the Research directory or null if not configured
52
+ */
53
+ export declare function getResearchDirPath(config: LogbookConfig, cwd?: string): string | null;
@@ -21,6 +21,8 @@ export const DEFAULT_CONFIG = {
21
21
  logbookDir: 'logbook',
22
22
  outputDir: 'dist/logbook',
23
23
  primaryColor: '#2563eb',
24
+ adrDir: 'docs/adr',
25
+ researchDir: 'docs/research',
24
26
  tags: {
25
27
  allowed: ['#bugfix', '#feature', '#refactor'],
26
28
  },
@@ -31,7 +33,14 @@ export function getConfig(cwd = process.cwd()) {
31
33
  if (existsSync(configPath)) {
32
34
  try {
33
35
  const userConfig = JSON.parse(readFileSync(configPath, 'utf8'));
34
- config = { ...DEFAULT_CONFIG, ...userConfig };
36
+ config = {
37
+ ...DEFAULT_CONFIG,
38
+ ...userConfig,
39
+ tags: {
40
+ ...DEFAULT_CONFIG.tags,
41
+ ...(userConfig.tags || {}),
42
+ },
43
+ };
35
44
  }
36
45
  catch (err) {
37
46
  const errorMessage = err instanceof Error ? err.message : String(err);
@@ -63,3 +72,22 @@ export function getLogbookDirPath(config, cwd = process.cwd()) {
63
72
  export function getOutputDirPath(config, cwd = process.cwd()) {
64
73
  return join(cwd, config.outputDir);
65
74
  }
75
+ /**
76
+ * Get the absolute path to the ADR directory.
77
+ * @param config - The logbook config
78
+ * @param cwd - Optional working directory (defaults to process.cwd())
79
+ * @returns Absolute path to the ADR directory or null if not configured
80
+ */
81
+ export function getAdrDirPath(config, cwd = process.cwd()) {
82
+ return config.adrDir ? join(cwd, config.adrDir) : null;
83
+ }
84
+ /**
85
+ * Get the absolute path to the Research directory.
86
+ * @param config - The logbook config
87
+ * @param cwd - Optional working directory (defaults to process.cwd())
88
+ * @returns Absolute path to the Research directory or null if not configured
89
+ */
90
+ export function getResearchDirPath(config, cwd = process.cwd()) {
91
+ return config.researchDir ? join(cwd, config.researchDir) : null;
92
+ }
93
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/lib/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { TimelineResearchItem } from './template-types.js';
2
+ export declare const renderCustomCard: (e: TimelineResearchItem, folderName?: string, indicatorLabel?: string) => string;
@@ -0,0 +1,58 @@
1
+ import { escapeHtml } from './html-escape.js';
2
+ import { html, renderTagsAndWorkspaces } from './template-helpers.js';
3
+ import { formatAbsoluteDate } from '../utils/date.js';
4
+ function getCustomStatusInfo(status) {
5
+ let badgeClass = 'tag-badge';
6
+ let icon = '💡';
7
+ const norm = status.toLowerCase();
8
+ if (norm === 'completed') {
9
+ badgeClass += ' tag-feature';
10
+ icon = '✅';
11
+ }
12
+ else if (norm === 'active') {
13
+ badgeClass += ' tag-refactor';
14
+ icon = '⚡';
15
+ }
16
+ else if (norm === 'paused') {
17
+ badgeClass += ' tag-superseded';
18
+ icon = '⏸';
19
+ }
20
+ else if (norm === 'postponed') {
21
+ badgeClass += ' tag-bugfix';
22
+ icon = '⏳';
23
+ }
24
+ else {
25
+ badgeClass += ' tag-workspace';
26
+ }
27
+ return { badgeClass, icon };
28
+ }
29
+ export const renderCustomCard = (e, folderName = 'research', indicatorLabel = 'RESEARCH') => {
30
+ const ticket = e.ticket ? escapeHtml(e.ticket) : '';
31
+ const title = e.title ? escapeHtml(e.title) : '';
32
+ const prompter = e.prompter ? escapeHtml(e.prompter) : '';
33
+ const summary = e.summary ? escapeHtml(e.summary) : '';
34
+ const status = e.status || 'active';
35
+ const displayDate = escapeHtml(e.displayDate);
36
+ const { badgeClass: statusBadgeClass, icon: statusIcon } = getCustomStatusInfo(status);
37
+ return html `<div class="timeline-item">
38
+ <a class="timeline-card timeline-card--adr" href="./${escapeHtml(folderName)}/${e.slug}/index.html"
39
+ ><div class="item-content">
40
+ <div class="item-meta">
41
+ <span>Last Updated: ${escapeHtml(formatAbsoluteDate(e.updated || e.dateStart))}</span> • Created on
42
+ ${displayDate} ${prompter ? `by ${prompter}` : ''}
43
+ </div>
44
+ <h3 class="item-title">
45
+ <span class="${statusBadgeClass} adr-status-badge">${statusIcon} ${status}</span>
46
+ ${ticket ? `${ticket}: ` : ''}${title}
47
+ </h3>
48
+ <div class="item-summary">${summary}</div>
49
+ <div class="tags-wrapper">
50
+ <span class="tag-badge adr-indicator-badge">${escapeHtml(indicatorLabel)}</span>
51
+ ${renderTagsAndWorkspaces(e.tags, e.workspaces)}
52
+ </div>
53
+ </div>
54
+ <span class="item-arrow">›</span></a
55
+ >
56
+ </div>`;
57
+ };
58
+ //# sourceMappingURL=custom-card-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-card-renderer.js","sourceRoot":"","sources":["../../src/lib/custom-card-renderer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,uBAAuB,EAAE,MAAM,uBAAuB,CAAC;AACtE,OAAO,EAAE,kBAAkB,EAAE,MAAM,kBAAkB,CAAC;AAGtD,SAAS,mBAAmB,CAAC,MAAc;IACzC,IAAI,UAAU,GAAG,WAAW,CAAC;IAC7B,IAAI,IAAI,GAAG,IAAI,CAAC;IAChB,MAAM,IAAI,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC;IAClC,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QACzB,UAAU,IAAI,cAAc,CAAC;QAC7B,IAAI,GAAG,GAAG,CAAC;IACb,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,UAAU,IAAI,eAAe,CAAC;QAC9B,IAAI,GAAG,GAAG,CAAC;IACb,CAAC;SAAM,IAAI,IAAI,KAAK,QAAQ,EAAE,CAAC;QAC7B,UAAU,IAAI,iBAAiB,CAAC;QAChC,IAAI,GAAG,GAAG,CAAC;IACb,CAAC;SAAM,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;QAChC,UAAU,IAAI,aAAa,CAAC;QAC5B,IAAI,GAAG,GAAG,CAAC;IACb,CAAC;SAAM,CAAC;QACN,UAAU,IAAI,gBAAgB,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAC9B,CAAC;AAED,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,CAAuB,EACvB,UAAU,GAAG,UAAU,EACvB,cAAc,GAAG,UAAU,EACnB,EAAE;IACV,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACpD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACjD,MAAM,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IAC1D,MAAM,OAAO,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;IACvD,MAAM,MAAM,GAAG,CAAC,CAAC,MAAM,IAAI,QAAQ,CAAC;IACpC,MAAM,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAE9C,MAAM,EAAE,UAAU,EAAE,gBAAgB,EAAE,IAAI,EAAE,UAAU,EAAE,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAEvF,OAAO,IAAI,CAAA;0DAC6C,UAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,IAAI;;;gCAG1D,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC;YAC5E,WAAW,IAAI,QAAQ,CAAC,CAAC,CAAC,MAAM,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE;;;yBAGlC,gBAAgB,sBAAsB,UAAU,IAAI,MAAM;YACvE,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK;;oCAEX,OAAO;;wDAEa,UAAU,CAAC,cAAc,CAAC;YACtE,uBAAuB,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,UAAU,CAAC;;;;;SAKhD,CAAC;AACV,CAAC,CAAC"}
@@ -0,0 +1,4 @@
1
+ import type { LogbookConfig } from './config.js';
2
+ import type { TimelineResearchItem } from './template-types.js';
3
+ import type { ActiveEntryResult } from './session.js';
4
+ export declare function loadCustomTypesData(config: LogbookConfig, projectRoot: string, activeEntry: ActiveEntryResult | undefined, availableWorkspaces: string[]): Promise<Record<string, TimelineResearchItem[]>>;
@@ -0,0 +1,37 @@
1
+ import fs from 'fs-extra';
2
+ import { join } from 'node:path';
3
+ import { processTimelineEntries } from './build-steps.js';
4
+ import { getLogbookEntries } from '../utils/fs.js';
5
+ export async function loadCustomTypesData(config, projectRoot, activeEntry, availableWorkspaces) {
6
+ const customTypesData = {};
7
+ if (config.customTypes) {
8
+ for (const [typeName, typeConfig] of Object.entries(config.customTypes)) {
9
+ const typeDir = join(projectRoot, typeConfig.dir);
10
+ if (await fs.pathExists(typeDir)) {
11
+ const rawEntries = await getLogbookEntries(typeDir);
12
+ const processed = (await processTimelineEntries(rawEntries, activeEntry, availableWorkspaces)).map((entry) => {
13
+ const raw = rawEntries.find((r) => r.slug === entry.slug);
14
+ const status = String(raw?.data?.status || 'active');
15
+ const updated = String(raw?.data?.updated || entry.dateStart);
16
+ return {
17
+ ...entry,
18
+ status,
19
+ updated,
20
+ };
21
+ });
22
+ const sortBy = typeConfig.sortBy || 'dateStart';
23
+ processed.sort((a, b) => {
24
+ const timeA = new Date(sortBy === 'updated' ? a.updated || a.dateStart : a.dateStart).getTime();
25
+ const timeB = new Date(sortBy === 'updated' ? b.updated || b.dateStart : b.dateStart).getTime();
26
+ return timeB - timeA;
27
+ });
28
+ customTypesData[typeName] = processed;
29
+ }
30
+ else {
31
+ customTypesData[typeName] = [];
32
+ }
33
+ }
34
+ }
35
+ return customTypesData;
36
+ }
37
+ //# sourceMappingURL=custom-types-builder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"custom-types-builder.js","sourceRoot":"","sources":["../../src/lib/custom-types-builder.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAC1D,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,CAAC,KAAK,UAAU,mBAAmB,CACvC,MAAqB,EACrB,WAAmB,EACnB,WAA0C,EAC1C,mBAA6B;IAE7B,MAAM,eAAe,GAA2C,EAAE,CAAC;IACnE,IAAI,MAAM,CAAC,WAAW,EAAE,CAAC;QACvB,KAAK,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,WAAW,CAAC,EAAE,CAAC;YACxE,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,EAAE,UAAU,CAAC,GAAG,CAAC,CAAC;YAClD,IAAI,MAAM,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;gBACjC,MAAM,UAAU,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,CAAC;gBACpD,MAAM,SAAS,GAAG,CAAC,MAAM,sBAAsB,CAAC,UAAU,EAAE,WAAW,EAAE,mBAAmB,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;oBAC3G,MAAM,GAAG,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;oBAC1D,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,IAAI,QAAQ,CAAC,CAAC;oBACrD,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,IAAI,KAAK,CAAC,SAAS,CAAC,CAAC;oBAC9D,OAAO;wBACL,GAAG,KAAK;wBACR,MAAM;wBACN,OAAO;qBACR,CAAC;gBACJ,CAAC,CAAC,CAAC;gBAEH,MAAM,MAAM,GAAG,UAAU,CAAC,MAAM,IAAI,WAAW,CAAC;gBAChD,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;oBACtB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;oBAChG,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;oBAChG,OAAO,KAAK,GAAG,KAAK,CAAC;gBACvB,CAAC,CAAC,CAAC;gBAEH,eAAe,CAAC,QAAQ,CAAC,GAAG,SAAS,CAAC;YACxC,CAAC;iBAAM,CAAC;gBACN,eAAe,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC;YACjC,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,eAAe,CAAC;AACzB,CAAC"}
@@ -24,3 +24,4 @@ export function getEntryDisplayId(ticket, slug) {
24
24
  export function getEntryDisplayTitle(title, slug) {
25
25
  return title || slug;
26
26
  }
27
+ //# sourceMappingURL=entry-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-id.js","sourceRoot":"","sources":["../../src/lib/entry-id.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAA0B,EAAE,IAAY;IACxE,OAAO,MAAM,IAAI,IAAI,CAAC;AACxB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,oBAAoB,CAAC,KAAyB,EAAE,IAAY;IAC1E,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC"}
@@ -53,3 +53,4 @@ export function getEntryFilePaths(logbookDir, entrySlug) {
53
53
  log: getEntryLogPath(logbookDir, entrySlug),
54
54
  };
55
55
  }
56
+ //# sourceMappingURL=entry-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entry-paths.js","sourceRoot":"","sources":["../../src/lib/entry-paths.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;GAGG;AAEH;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,SAAiB;IAChE,OAAO,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;AACrC,CAAC;AAED;;;;;GAKG;AACH,SAAS,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;IAC9D,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;AAC/D,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,UAAkB,EAAE,SAAiB;IAC/D,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,WAAW,CAAC,CAAC;AAChE,CAAC;AAED;;;;;GAKG;AACH,SAAS,eAAe,CAAC,UAAkB,EAAE,SAAiB;IAC5D,OAAO,IAAI,CAAC,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC7D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,UAAkB,EAAE,SAAiB;IACrE,OAAO;QACL,KAAK,EAAE,iBAAiB,CAAC,UAAU,EAAE,SAAS,CAAC;QAC/C,MAAM,EAAE,kBAAkB,CAAC,UAAU,EAAE,SAAS,CAAC;QACjD,GAAG,EAAE,eAAe,CAAC,UAAU,EAAE,SAAS,CAAC;KAC5C,CAAC;AACJ,CAAC"}