project-logbook 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/dist/commands/answer.d.ts +1 -0
  2. package/dist/commands/answer.js +41 -0
  3. package/dist/commands/answer.js.map +1 -0
  4. package/dist/commands/answers.d.ts +6 -0
  5. package/dist/commands/answers.js +37 -0
  6. package/dist/commands/answers.js.map +1 -0
  7. package/dist/commands/ask.d.ts +1 -0
  8. package/dist/commands/ask.js +30 -0
  9. package/dist/commands/ask.js.map +1 -0
  10. package/dist/commands/build.js +18 -2
  11. package/dist/commands/build.js.map +1 -1
  12. package/dist/commands/deprecate-qna.d.ts +1 -0
  13. package/dist/commands/deprecate-qna.js +33 -0
  14. package/dist/commands/deprecate-qna.js.map +1 -0
  15. package/dist/commands/status.js +3 -0
  16. package/dist/commands/status.js.map +1 -1
  17. package/dist/commands/unanswered.d.ts +1 -0
  18. package/dist/commands/unanswered.js +25 -0
  19. package/dist/commands/unanswered.js.map +1 -0
  20. package/dist/index.js +37 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/lib/config.d.ts +8 -0
  23. package/dist/lib/config.js +10 -0
  24. package/dist/lib/config.js.map +1 -1
  25. package/dist/lib/template-helpers.d.ts +2 -1
  26. package/dist/lib/template-helpers.js +29 -0
  27. package/dist/lib/template-helpers.js.map +1 -1
  28. package/dist/lib/template-types.d.ts +12 -0
  29. package/dist/lib/templates.js +9 -1
  30. package/dist/lib/templates.js.map +1 -1
  31. package/dist/linters/index.js +2 -0
  32. package/dist/linters/index.js.map +1 -1
  33. package/dist/linters/qna.d.ts +3 -0
  34. package/dist/linters/qna.js +120 -0
  35. package/dist/linters/qna.js.map +1 -0
  36. package/dist/templates/hub.js +123 -8
  37. package/dist/templates/qna.md +11 -0
  38. package/dist/templates/steer.txt +2 -0
  39. package/dist/templates/styles.css +53 -0
  40. package/dist/utils/qna.d.ts +46 -0
  41. package/dist/utils/qna.js +157 -0
  42. package/dist/utils/qna.js.map +1 -0
  43. package/dist/watch/__tests__/git-utils.test.d.ts +1 -0
  44. package/dist/watch/__tests__/git-utils.test.js +17 -0
  45. package/dist/watch/__tests__/git-utils.test.js.map +1 -0
  46. package/dist/watch/collectors/acli.js +2 -2
  47. package/dist/watch/collectors/acli.js.map +1 -1
  48. package/dist/watch/collectors/entries.js +3 -3
  49. package/dist/watch/collectors/entries.js.map +1 -1
  50. package/dist/watch/collectors/glab.js +126 -37
  51. package/dist/watch/collectors/glab.js.map +1 -1
  52. package/dist/watch/collectors/jira.js +3 -2
  53. package/dist/watch/collectors/jira.js.map +1 -1
  54. package/dist/watch/dashboard-html.js +8 -0
  55. package/dist/watch/dashboard-html.js.map +1 -1
  56. package/dist/watch/git-utils.d.ts +10 -4
  57. package/dist/watch/git-utils.js +22 -6
  58. package/dist/watch/git-utils.js.map +1 -1
  59. package/dist/watch/hub-state.d.ts +3 -1
  60. package/dist/watch/hub-state.js +2 -0
  61. package/dist/watch/hub-state.js.map +1 -1
  62. package/dist/watch/hub.js +35 -2
  63. package/dist/watch/hub.js.map +1 -1
  64. package/dist/watch/state.d.ts +5 -1
  65. package/dist/watch/state.js.map +1 -1
  66. package/dist/watch/types.d.ts +21 -0
  67. package/dist/watch/types.js.map +1 -1
  68. package/dist/watch/watcher.js +13 -2
  69. package/dist/watch/watcher.js.map +1 -1
  70. package/package.json +7 -2
  71. package/src/templates/hub.js +123 -8
  72. package/src/templates/qna.md +11 -0
  73. package/src/templates/steer.txt +2 -0
  74. package/src/templates/styles.css +53 -0
@@ -1,6 +1,6 @@
1
1
  import { ATTR_PAGE_SLUG } from './html-attributes.js';
2
2
  import { escapeHtml } from './html-escape.js';
3
- import { html, renderTimelineEntryItem, renderTimelineCommitItem, renderTimelineTagItem, renderTimelineAdrItem, paginationLinks, } from './template-helpers.js';
3
+ import { html, renderTimelineEntryItem, renderTimelineCommitItem, renderTimelineTagItem, renderTimelineAdrItem, renderQnaCard, paginationLinks, } from './template-helpers.js';
4
4
  import { renderCustomCard } from './custom-card-renderer.js';
5
5
  import { autolinkReferences, getRegisteredResearchEntries, linkGitlabReferences } from './autolink.js';
6
6
  export { postTemplate } from './post-template.js';
@@ -96,6 +96,14 @@ export const timelineTemplate = (props) => {
96
96
  }
97
97
  }
98
98
  }
99
+ if (props.qnaItems && props.qnaItems.length > 0) {
100
+ const unansweredCount = props.qnaItems.filter((q) => q.status === 'asked').length;
101
+ const tabTitle = unansweredCount > 0 ? `QnA (${unansweredCount})` : 'QnA';
102
+ const qnaTabHtml = html `
103
+ <div class="timeline">${props.qnaItems.map((item) => renderQnaCard(item)).join('')}</div>
104
+ `;
105
+ tabs.push({ id: 'qna', title: tabTitle, content: qnaTabHtml });
106
+ }
99
107
  tabs.push({ id: 'readme', title: 'Project Readme', content: readmeHtml || '<p>No README available.</p>' }, { id: 'about', title: 'About this Logbook', content: aboutHtml || '<p>No information available.</p>' });
100
108
  const { tabsHtml, sectionsHtml } = tabsComponent(tabs, 0);
101
109
  const header = html `<header>
@@ -1 +1 @@
1
- {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/lib/templates.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/lib/templates.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EACL,IAAI,EACJ,uBAAuB,EACvB,wBAAwB,EACxB,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,eAAe,GAChB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,kBAAkB,EAAE,4BAA4B,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AAEvG,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAElD,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,IAAiB,EAAE,gBAAwB,CAAC,EAAE,EAAE;IAC5E,MAAM,QAAQ,GAAG,IAAI;SAClB,GAAG,CACF,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,mBAAmB,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,+BAA+B,KAAK,KAAK,aAAa,oBAAoB,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,eAAe,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,8BAA8B,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,OAAO,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,CAChS;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,MAAM,YAAY,GAAG,IAAI;SACtB,GAAG,CACF,CAAC,GAAG,EAAE,KAAK,EAAE,EAAE,CACb,YAAY,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,oDAAoD,KAAK,KAAK,aAAa,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,eAAe,KAAK,GAAG,CAAC,OAAO,QAAQ,CAC3J;SACA,IAAI,CAAC,EAAE,CAAC,CAAC;IACZ,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,CAAC;AACpC,CAAC,CAAC;AAeF,MAAM,CAAC,MAAM,MAAM,GAAG,CAAC,EACrB,KAAK,EACL,MAAM,EACN,OAAO,EACP,WAAW,EACX,QAAQ,GAAG,IAAI,EACf,WAAW,EACX,QAAQ,EACR,SAAS,EACT,cAAc,GAAG,KAAK,EACtB,OAAO,EACP,OAAO,GACK,EAAE,EAAE;IAChB,MAAM,eAAe,GACnB,WAAW;QACX,8FAA8F,WAAW,GAAG,CAAC;IAE/G,MAAM,yBAAyB,GAAG,4BAA4B,EAAE,CAAC;IACjE,IAAI,eAAe,GACjB,yBAAyB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,MAAM,EAAE,yBAAyB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAClH,IAAI,gBAAgB,GAClB,yBAAyB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;IACpH,eAAe,GAAG,oBAAoB,CAAC,eAAe,CAAC,CAAC;IACxD,gBAAgB,GAAG,oBAAoB,CAAC,gBAAgB,CAAC,CAAC;IAE1D,OAAO,IAAI,CAAA;;;;aAIA,UAAU,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,WAAW,CAAC;wCACnB,UAAU,CAAC,eAAe,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC;MACvG,cAAc,CAAC,CAAC,CAAC,gGAAgG,QAAQ,WAAW,CAAC,CAAC,CAAC,EAAE;MACzI,OAAO,CAAC,CAAC,CAAC,0BAA0B,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;MAChE,OAAO,CAAC,CAAC,CAAC,0BAA0B,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;kDACpB,QAAQ;mCACvB,QAAQ;;OAEpC,QAAQ,CAAC,CAAC,CAAC,IAAI,cAAc,KAAK,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE;MACnD,eAAe,SAAS,gBAAgB;;QAEtC,SAAS,CAAC,CAAC,CAAC,yBAAyB,SAAS,GAAG,cAAc,CAAC,CAAC,CAAC,eAAe,QAAQ,uBAAuB,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE;;mBAErH,QAAQ;eACZ,CAAC;AAChB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAC9B,KAA2D,EACtB,EAAE;IACvC,MAAM,EACJ,WAAW,EACX,MAAM,EACN,UAAU,EACV,SAAS,EACT,WAAW,EACX,WAAW,EACX,UAAU,EACV,aAAa,EACb,WAAW,EACX,UAAU,GACX,GAAG,KAAK,CAAC;IACV,MAAM,iBAAiB,GAAG,WAAW;QACnC,CAAC,CAAC;;+CAEyC,WAAW;cAC5C;QACV,CAAC,CAAC,EAAE,CAAC;IACP,MAAM,WAAW,GAAG,IAAI,CAAA,GAAG,iBAAiB;;QAEtC,MAAM;SACL,GAAG,CACF,CAAC,CAAgB,EAAE,EAAE,CACnB,IAAI,CAAA;oBACI,CAAC,CAAC,KAAK;gBACX,CAAC,CAAC,KAAK;SACN,GAAG,CAAC,CAAC,IAAkB,EAAE,EAAE;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO;YAAE,OAAO,uBAAuB,CAAC,IAAI,CAAC,CAAC;QAChE,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,qBAAqB,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;QAC3E,IAAI,IAAI,CAAC,IAAI,KAAK,KAAK;YAAE,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC;QAC5D,OAAO,wBAAwB,CAAC,IAAI,EAAE,WAAW,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC;IAChF,CAAC,CAAC;SACD,IAAI,CAAC,EAAE,CAAC;uBACF,CACd;SACA,IAAI,CAAC,EAAE,CAAC;QACT,eAAe,CAAC,WAAY,EAAE,UAAW,CAAC;WACvC,CAAC;IACV,MAAM,IAAI,GAAG,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,WAAW,EAAE,CAAC,CAAC;IAE3E,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,UAAU,GAAG,IAAI,CAAA;8BACG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;KAC3F,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IAChE,CAAC;IAED,IAAI,KAAK,CAAC,eAAe,IAAI,KAAK,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9D,MAAM,YAAY,GAAG,IAAI,CAAA;;UAEnB,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,EAAE,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;KAE3F,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,KAAK,MAAM,GAAG,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACnC,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACtC,MAAM,OAAO,GAAG,IAAI,CAAA;;cAEd,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;;SAExF,CAAC;gBACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,KAAK,EAAE,GAAG,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;YAChE,CAAC;QACH,CAAC;IACH,CAAC;IAED,IAAI,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAChD,MAAM,eAAe,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;QAClF,MAAM,QAAQ,GAAG,eAAe,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,eAAe,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC;QAC1E,MAAM,UAAU,GAAG,IAAI,CAAA;8BACG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;KACnF,CAAC;QACF,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,EAAE,CAAC,CAAC;IACjE,CAAC;IAED,IAAI,CAAC,IAAI,CACP,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,EAAE,OAAO,EAAE,UAAU,IAAI,6BAA6B,EAAE,EAC/F,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,OAAO,EAAE,SAAS,IAAI,kCAAkC,EAAE,CACvG,CAAC;IAEF,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,GAAG,aAAa,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;IAC1D,MAAM,MAAM,GAAG,IAAI,CAAA;;mCAEc,UAAU,CAAC,WAAW,CAAC;;;kFAGwB,QAAQ;;;YAG9E,CAAC;IACX,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,YAAY,EAAE,CAAC;AAC3C,CAAC,CAAC"}
@@ -5,6 +5,7 @@ import links from './links.js';
5
5
  import lockfile from './lockfile.js';
6
6
  import placeholders from './placeholders.js';
7
7
  import projectIntegrity from './project-integrity.js';
8
+ import qna from './qna.js';
8
9
  import readability from './readability.js';
9
10
  import technicalLog from './technical-log.js';
10
11
  import workspaces from './workspaces.js';
@@ -16,6 +17,7 @@ export const linters = [
16
17
  lockfile,
17
18
  placeholders,
18
19
  projectIntegrity,
20
+ qna,
19
21
  readability,
20
22
  technicalLog,
21
23
  workspaces,
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/linters/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,wBAAwB,CAAC;AACrD,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAC3C,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAC3C,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,eAAe;IACf,WAAW;IACX,UAAU;IACV,KAAK;IACL,QAAQ;IACR,YAAY;IACZ,gBAAgB;IAChB,WAAW;IACX,YAAY;IACZ,UAAU;CACX,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/linters/index.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,MAAM,wBAAwB,CAAC;AACrD,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAC3C,OAAO,UAAU,MAAM,kBAAkB,CAAC;AAC1C,OAAO,KAAK,MAAM,YAAY,CAAC;AAC/B,OAAO,QAAQ,MAAM,eAAe,CAAC;AACrC,OAAO,YAAY,MAAM,mBAAmB,CAAC;AAC7C,OAAO,gBAAgB,MAAM,wBAAwB,CAAC;AACtD,OAAO,GAAG,MAAM,UAAU,CAAC;AAC3B,OAAO,WAAW,MAAM,kBAAkB,CAAC;AAC3C,OAAO,YAAY,MAAM,oBAAoB,CAAC;AAC9C,OAAO,UAAU,MAAM,iBAAiB,CAAC;AAEzC,MAAM,CAAC,MAAM,OAAO,GAAG;IACrB,eAAe;IACf,WAAW;IACX,UAAU;IACV,KAAK;IACL,QAAQ;IACR,YAAY;IACZ,gBAAgB;IAChB,GAAG;IACH,WAAW;IACX,YAAY;IACZ,UAAU;CACX,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { Linter } from '../lib/lint-types.js';
2
+ declare const linter: Linter;
3
+ export default linter;
@@ -0,0 +1,120 @@
1
+ import fs from 'fs-extra';
2
+ import { join } from 'node:path';
3
+ import matter from 'gray-matter';
4
+ import { getQnaDirPath } from '../lib/config.js';
5
+ import { parseQnaContent } from '../utils/qna.js';
6
+ const linter = {
7
+ name: 'qna',
8
+ description: 'Validates QnA file naming, frontmatter, and mandatory subtitles',
9
+ async check(context) {
10
+ if (context.entryName)
11
+ return [];
12
+ const issues = [];
13
+ const qnaDir = getQnaDirPath(context.config) || join(process.cwd(), 'docs/qna');
14
+ if (!(await fs.pathExists(qnaDir))) {
15
+ return issues;
16
+ }
17
+ const files = await fs.readdir(qnaDir);
18
+ for (const file of files) {
19
+ const filePath = join(qnaDir, file);
20
+ const stat = await fs.stat(filePath);
21
+ if (stat.isDirectory())
22
+ continue;
23
+ if (!file.endsWith('.md')) {
24
+ issues.push({
25
+ level: 'warning',
26
+ category: 'qna',
27
+ message: `${file}: Unexpected non-markdown file in QnA directory.`,
28
+ });
29
+ continue;
30
+ }
31
+ if (!/^\d{5}-[\w-]+\.md$/.test(file)) {
32
+ issues.push({
33
+ level: 'error',
34
+ category: 'qna',
35
+ message: `${file}: File name must match format '00001-question-slug.md'`,
36
+ });
37
+ }
38
+ try {
39
+ const raw = await fs.readFile(filePath, 'utf8');
40
+ const { data, content } = matter(raw);
41
+ if (data.id === undefined || typeof data.id !== 'number') {
42
+ issues.push({
43
+ level: 'error',
44
+ category: 'qna',
45
+ message: `${file}: Missing or invalid numeric 'id' in frontmatter.`,
46
+ });
47
+ }
48
+ const validStatuses = ['asked', 'answered', 'deprecated'];
49
+ if (!data.status || !validStatuses.includes(String(data.status))) {
50
+ issues.push({
51
+ level: 'error',
52
+ category: 'qna',
53
+ message: `${file}: 'status' must be one of: asked, answered, deprecated.`,
54
+ });
55
+ }
56
+ if (!data.dateAsked) {
57
+ issues.push({
58
+ level: 'error',
59
+ category: 'qna',
60
+ message: `${file}: Missing required 'dateAsked' in frontmatter.`,
61
+ });
62
+ }
63
+ if (data.status === 'answered' && !data.dateAnswered) {
64
+ issues.push({
65
+ level: 'error',
66
+ category: 'qna',
67
+ message: `${file}: 'dateAnswered' is required when status is 'answered'.`,
68
+ });
69
+ }
70
+ if (data.status === 'deprecated' && !data.dateDeprecated) {
71
+ issues.push({
72
+ level: 'error',
73
+ category: 'qna',
74
+ message: `${file}: 'dateDeprecated' is required when status is 'deprecated'.`,
75
+ });
76
+ }
77
+ // Subtitle checks (markdown subtitles are mandatory)
78
+ if (!/###\s+Question/i.test(content)) {
79
+ issues.push({
80
+ level: 'error',
81
+ category: 'qna',
82
+ message: `${file}: Mandatory subtitle '### Question' is missing.`,
83
+ });
84
+ }
85
+ if (!/###\s+Answer/i.test(content)) {
86
+ issues.push({
87
+ level: 'error',
88
+ category: 'qna',
89
+ message: `${file}: Mandatory subtitle '### Answer' is missing.`,
90
+ });
91
+ }
92
+ const { question, answer } = parseQnaContent(content);
93
+ if (!question) {
94
+ issues.push({
95
+ level: 'error',
96
+ category: 'qna',
97
+ message: `${file}: Question content under '### Question' cannot be empty.`,
98
+ });
99
+ }
100
+ if (data.status === 'answered' && !answer) {
101
+ issues.push({
102
+ level: 'error',
103
+ category: 'qna',
104
+ message: `${file}: Answer content under '### Answer' is empty for answered question.`,
105
+ });
106
+ }
107
+ }
108
+ catch (err) {
109
+ issues.push({
110
+ level: 'error',
111
+ category: 'qna',
112
+ message: `${file}: Failed to parse markdown/frontmatter: ${err instanceof Error ? err.message : String(err)}`,
113
+ });
114
+ }
115
+ }
116
+ return issues;
117
+ },
118
+ };
119
+ export default linter;
120
+ //# sourceMappingURL=qna.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qna.js","sourceRoot":"","sources":["../../src/linters/qna.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAElD,MAAM,MAAM,GAAW;IACrB,IAAI,EAAE,KAAK;IACX,WAAW,EAAE,iEAAiE;IAC9E,KAAK,CAAC,KAAK,CAAC,OAAsB;QAChC,IAAI,OAAO,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QAEjC,MAAM,MAAM,GAAgB,EAAE,CAAC;QAC/B,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,UAAU,CAAC,CAAC;QAEhF,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;YACnC,OAAO,MAAM,CAAC;QAChB,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;QAEvC,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;YACpC,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;YACrC,IAAI,IAAI,CAAC,WAAW,EAAE;gBAAE,SAAS;YAEjC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;gBAC1B,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,SAAS;oBAChB,QAAQ,EAAE,KAAK;oBACf,OAAO,EAAE,GAAG,IAAI,kDAAkD;iBACnE,CAAC,CAAC;gBACH,SAAS;YACX,CAAC;YAED,IAAI,CAAC,oBAAoB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,OAAO;oBACd,QAAQ,EAAE,KAAK;oBACf,OAAO,EAAE,GAAG,IAAI,wDAAwD;iBACzE,CAAC,CAAC;YACL,CAAC;YAED,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;gBAChD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;gBAEtC,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS,IAAI,OAAO,IAAI,CAAC,EAAE,KAAK,QAAQ,EAAE,CAAC;oBACzD,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,mDAAmD;qBACpE,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,aAAa,GAAsB,CAAC,OAAO,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC;gBAC7E,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC;oBACjE,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,yDAAyD;qBAC1E,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,CAAC;oBACpB,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,gDAAgD;qBACjE,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;oBACrD,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,yDAAyD;qBAC1E,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,YAAY,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,CAAC;oBACzD,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,6DAA6D;qBAC9E,CAAC,CAAC;gBACL,CAAC;gBAED,qDAAqD;gBACrD,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrC,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,iDAAiD;qBAClE,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;oBACnC,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,+CAA+C;qBAChE,CAAC,CAAC;gBACL,CAAC;gBAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;gBACtD,IAAI,CAAC,QAAQ,EAAE,CAAC;oBACd,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,0DAA0D;qBAC3E,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,IAAI,CAAC,MAAM,KAAK,UAAU,IAAI,CAAC,MAAM,EAAE,CAAC;oBAC1C,MAAM,CAAC,IAAI,CAAC;wBACV,KAAK,EAAE,OAAO;wBACd,QAAQ,EAAE,KAAK;wBACf,OAAO,EAAE,GAAG,IAAI,qEAAqE;qBACtF,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,IAAI,CAAC;oBACV,KAAK,EAAE,OAAO;oBACd,QAAQ,EAAE,KAAK;oBACf,OAAO,EAAE,GAAG,IAAI,2CAA2C,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE;iBAC9G,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC;IAChB,CAAC;CACF,CAAC;AAEF,eAAe,MAAM,CAAC"}
@@ -176,8 +176,12 @@
176
176
 
177
177
  function renderTicketMeta(p) {
178
178
  var ticket = p.activeTicket || 'none';
179
- var ticketUrl =
180
- p.jiraBaseUrl && p.activeTicket
179
+ var isGl = ticket.charAt(0) === '#' && p.gitlabBaseUrl;
180
+ var ticketUrl = isGl
181
+ ? p.gitlabBaseUrl.replace(/\/+$/, '') +
182
+ '/' +
183
+ encodeURIComponent(ticket.replace(/^#/, '').match(/^\d+/)?.[0] || ticket)
184
+ : p.jiraBaseUrl && p.activeTicket
181
185
  ? p.jiraBaseUrl.replace(/\/+$/, '') + '/browse/' + encodeURIComponent(p.activeTicket)
182
186
  : null;
183
187
  var value = ticketUrl
@@ -187,7 +191,7 @@
187
191
  escapeHtml(ticket) +
188
192
  '<span class="link-icon">\u00A0&#x2197;</span></a>'
189
193
  : escapeHtml(ticket);
190
- var jiraLabel = p.jiraStatus ? ' <span class="jira-status">' + escapeHtml(p.jiraStatus) + '</span>' : '';
194
+ var jiraLabel = !isGl && p.jiraStatus ? ' <span class="jira-status">' + escapeHtml(p.jiraStatus) + '</span>' : '';
191
195
  return (
192
196
  '<div class="meta-item"><span class="meta-label">ticket:</span> <span class="meta-value">' +
193
197
  value +
@@ -506,10 +510,16 @@
506
510
  html +=
507
511
  '<table class="help-table"><thead><tr><th>Title</th><th>Status</th><th>Comments</th><th>Branch</th></tr></thead><tbody>';
508
512
  items.forEach(function (m) {
509
- var statusBadge =
510
- m.status === 'draft'
511
- ? '<span class="badge badge-info">Draft</span>'
512
- : '<span class="badge badge-success">Open</span>';
513
+ var statusBadge;
514
+ if (m.status === 'draft') {
515
+ statusBadge = '<span class="badge badge-info">Draft</span>';
516
+ } else if (m.status === 'merged') {
517
+ statusBadge = '<span class="badge badge-success">Merged</span>';
518
+ } else if (m.status === 'closed') {
519
+ statusBadge = '<span class="badge badge-warning">Closed</span>';
520
+ } else {
521
+ statusBadge = '<span class="badge badge-info">Open</span>';
522
+ }
513
523
  var branchUrl = projUrl ? projUrl.replace(/\/$/, '') + '/-/tree/' + encodeURIComponent(m.sourceBranch) : '';
514
524
  var branchCell = branchUrl
515
525
  ? '<a class="meta-link" href="' +
@@ -553,6 +563,87 @@
553
563
  loadGitLabMrs();
554
564
  setInterval(loadGitLabMrs, 5 * 60 * 1000);
555
565
 
566
+ // --- GitLab Work Items tab ---
567
+ function loadWorkItems() {
568
+ fetch('/_logbook/gitlab-work-items')
569
+ .then(function (r) {
570
+ return r.json();
571
+ })
572
+ .then(function (items) {
573
+ var container = document.getElementById('workitems-content');
574
+ var countEl4 = document.getElementById('workitems-count');
575
+ var count = items ? items.length : 0;
576
+ countEl4.textContent = count;
577
+ countEl4.style.display = count > 0 ? '' : 'none';
578
+ if (!items || items.length === 0) {
579
+ container.innerHTML =
580
+ '<div class="placeholder-msg">No open work items found.<br>Install the <code>glab</code> CLI and run <code>glab auth login</code> to connect.</div>';
581
+ return;
582
+ }
583
+ var grouped = {};
584
+ items.forEach(function (w) {
585
+ if (!grouped[w.projectUrl]) grouped[w.projectUrl] = [];
586
+ grouped[w.projectUrl].push(w);
587
+ });
588
+ var html = '';
589
+ for (var projUrl in grouped) {
590
+ if (!grouped.hasOwnProperty(projUrl)) continue;
591
+ var wis = grouped[projUrl];
592
+ var fullName = wis[0].projectName || projUrl;
593
+ var projName = fullName.includes(' / ') ? fullName.split(' / ').pop() : fullName;
594
+ html +=
595
+ '<h3 class="group-heading">' +
596
+ '<a href="' +
597
+ escapeHtml(projUrl) +
598
+ '" target="_blank" rel="noopener">' +
599
+ escapeHtml(projName) +
600
+ '<span class="group-link-icon"> &#x2197;</span></a></h3>';
601
+ html +=
602
+ '<table class="help-table"><thead><tr><th>#</th><th>Summary</th><th>State</th><th>Last Update</th></tr></thead><tbody>';
603
+ wis.forEach(function (w) {
604
+ var titleCell = w.url
605
+ ? '<a class="meta-link" href="' +
606
+ escapeHtml(w.url) +
607
+ '" target="_blank" rel="noopener">' +
608
+ escapeHtml(w.title) +
609
+ '<span class="link-icon">\u00A0&#x2197;</span></a>'
610
+ : escapeHtml(w.title);
611
+ var stateBadge =
612
+ w.status === 'opened'
613
+ ? '<span class="badge badge-info">Open</span>'
614
+ : '<span class="badge badge-' +
615
+ (w.status === 'closed' ? 'success' : 'warning') +
616
+ '">' +
617
+ escapeHtml(w.status) +
618
+ '</span>';
619
+ html +=
620
+ '<tr>' +
621
+ '<td class="cell-mono">#' +
622
+ w.iid +
623
+ '</td>' +
624
+ '<td>' +
625
+ titleCell +
626
+ '</td>' +
627
+ '<td>' +
628
+ stateBadge +
629
+ '</td>' +
630
+ '<td class="cell-time">' +
631
+ relativeTime(w.updated) +
632
+ '</td>' +
633
+ '</tr>';
634
+ });
635
+ html += '</tbody></table>';
636
+ }
637
+ container.innerHTML = html;
638
+ })
639
+ .catch(function () {
640
+ document.getElementById('workitems-content').innerHTML =
641
+ '<div class="placeholder-msg">Failed to load work items.</div>';
642
+ });
643
+ }
644
+ loadWorkItems();
645
+ setInterval(loadWorkItems, 5 * 60 * 1000);
646
+
556
647
  // --- SSE connection ---
557
648
  function connect() {
558
649
  var es = new EventSource('/_logbook/events');
@@ -565,6 +656,20 @@
565
656
  var data = JSON.parse(e.data);
566
657
  if (data.type === 'status-update') {
567
658
  projects[data.project.projectPath] = data.project;
659
+ console.log(
660
+ '[logbook-debug] status-update',
661
+ data.project.projectPath,
662
+ 'mrStatus=',
663
+ data.project.mrStatus,
664
+ 'mrUrl=',
665
+ data.project.mrUrl,
666
+ 'statuses=',
667
+ (data.project.statuses || [])
668
+ .map(function (s) {
669
+ return s.id;
670
+ })
671
+ .join(','),
672
+ );
568
673
  renderProjects();
569
674
  updateTabFooters();
570
675
  } else if (data.type === 'project-removed') {
@@ -572,7 +677,17 @@
572
677
  delete projectEvents[data.projectPath];
573
678
  renderProjects();
574
679
  } else if (data.type === 'full-state') {
575
- for (var i = 0; i < data.projects.length; i++) projects[data.projects[i].projectPath] = data.projects[i];
680
+ for (var i = 0; i < data.projects.length; i++) {
681
+ projects[data.projects[i].projectPath] = data.projects[i];
682
+ console.log(
683
+ '[logbook-debug] full-state',
684
+ data.projects[i].projectPath,
685
+ 'mrStatus=',
686
+ data.projects[i].mrStatus,
687
+ 'mrUrl=',
688
+ data.projects[i].mrUrl,
689
+ );
690
+ }
576
691
  if (data.events)
577
692
  for (var path in data.events) {
578
693
  if (data.events.hasOwnProperty(path)) projectEvents[path] = data.events[path];
@@ -0,0 +1,11 @@
1
+ ---
2
+ id: {{id}}
3
+ status: asked
4
+ dateAsked: {{dateAsked}}
5
+ ---
6
+
7
+ ### Question
8
+ {{question}}
9
+
10
+ ### Answer
11
+
@@ -6,6 +6,7 @@ Do NOT run `logbook new` or `logbook release` — these are prompter responsibil
6
6
  Phase 1: Understand
7
7
  1. Read `ticket.md` in the active entry. This is your source of truth for requirements.
8
8
  2. Review `AGENTS.md` and `CONTRIBUTING.md` if you need architectural or workflow context.
9
+ 3. Check existing QnAs with `logbook answers` or check open questions with `logbook unanswered`.
9
10
 
10
11
  Phase 2: Execute & Trace
11
12
 
@@ -29,6 +30,7 @@ You MUST log your work continuously using `logbook log "<message>"`. This is not
29
30
  3. **One message per command call.** For multiple entries, call the command multiple times.
30
31
  4. Do NOT reconstruct the log at the end. If the log is empty when you finish, you did it wrong.
31
32
  5. Stick to the active entry; never modify past entries in the logbook folder.
33
+ 6. For short technical questions and findings, record them via `logbook ask "<question>"` and resolve them with `logbook answer <id> "<answer>"`.
32
34
 
33
35
  Phase 3: Synthesize
34
36
  1. When implementation is finished, write the narrative in `index.md`.
@@ -578,6 +578,16 @@ footer .build-time a:hover {
578
578
  color: #4b5563;
579
579
  }
580
580
 
581
+ .tag-asked {
582
+ background: #fef3c7;
583
+ color: #92400e;
584
+ }
585
+
586
+ .tag-answered {
587
+ background: #dcfce7;
588
+ color: #166534;
589
+ }
590
+
581
591
  /* Timeline card tag styling */
582
592
  .timeline-card .tags-wrapper {
583
593
  margin-top: 0.5rem;
@@ -1017,3 +1027,46 @@ article img[src$='.svg'] {
1017
1027
  .timeline-card--adr .tags-wrapper {
1018
1028
  margin-top: 0.5rem;
1019
1029
  }
1030
+
1031
+ /* QnA card styles */
1032
+ .qna-card {
1033
+ display: block;
1034
+ background: var(--card-bg, #ffffff);
1035
+ border: 1px solid var(--border, #e5e7eb);
1036
+ border-radius: 8px;
1037
+ padding: 1.25rem 1.5rem;
1038
+ margin-bottom: 1.5rem;
1039
+ }
1040
+
1041
+ .qna-card.unanswered {
1042
+ border-left: 4px solid #f59e0b;
1043
+ }
1044
+
1045
+ .qna-card.answered {
1046
+ border-left: 4px solid #10b981;
1047
+ }
1048
+
1049
+ .qna-meta {
1050
+ font-size: 0.8125rem;
1051
+ color: var(--text-muted);
1052
+ margin-bottom: 0.75rem;
1053
+ display: flex;
1054
+ align-items: center;
1055
+ gap: 0.5rem;
1056
+ flex-wrap: wrap;
1057
+ }
1058
+
1059
+ .qna-card h3 {
1060
+ font-size: 0.95rem;
1061
+ font-weight: 700;
1062
+ margin: 0.75rem 0 0.25rem 0;
1063
+ text-transform: uppercase;
1064
+ letter-spacing: 0.05em;
1065
+ color: var(--text-muted);
1066
+ }
1067
+
1068
+ .qna-card .markdown-body {
1069
+ color: var(--text);
1070
+ font-size: 0.9375rem;
1071
+ line-height: 1.6;
1072
+ }
@@ -0,0 +1,46 @@
1
+ export type QnaStatus = 'asked' | 'answered' | 'deprecated';
2
+ export interface QnaItem {
3
+ id: number;
4
+ status: QnaStatus;
5
+ dateAsked: string;
6
+ dateAnswered?: string;
7
+ dateDeprecated?: string;
8
+ question: string;
9
+ answer: string;
10
+ questionHtml?: string;
11
+ answerHtml?: string;
12
+ filename: string;
13
+ filepath: string;
14
+ }
15
+ export declare function resolveQnaEntry(qnaDir: string, idInput: string): Promise<{
16
+ id: number;
17
+ item: QnaItem;
18
+ filepath: string;
19
+ } | {
20
+ error: string;
21
+ }>;
22
+ export declare function sortQnaItems<T extends {
23
+ status: QnaStatus;
24
+ dateAsked: string;
25
+ id: number;
26
+ }>(items: T[]): T[];
27
+ export declare function slugifyQuestion(text: string, maxLen?: number): string;
28
+ export declare function parseQnaContent(body: string): {
29
+ question: string;
30
+ answer: string;
31
+ };
32
+ export declare function formatQnaMarkdown(params: {
33
+ id: number;
34
+ status: QnaStatus;
35
+ dateAsked: string;
36
+ dateAnswered?: string;
37
+ dateDeprecated?: string;
38
+ question: string;
39
+ answer?: string;
40
+ }): string;
41
+ export declare function getAllQna(qnaDir: string): Promise<QnaItem[]>;
42
+ export declare function getNextQnaId(qnaDir: string): Promise<number>;
43
+ export declare function findQnaById(qnaDir: string, id: number): Promise<{
44
+ item: QnaItem;
45
+ filepath: string;
46
+ } | null>;