project-logbook 1.2.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.
- package/README.md +2 -0
- package/dist/commands/answer.d.ts +1 -0
- package/dist/commands/answer.js +41 -0
- package/dist/commands/answer.js.map +1 -0
- package/dist/commands/answers.d.ts +6 -0
- package/dist/commands/answers.js +37 -0
- package/dist/commands/answers.js.map +1 -0
- package/dist/commands/ask.d.ts +1 -0
- package/dist/commands/ask.js +30 -0
- package/dist/commands/ask.js.map +1 -0
- package/dist/commands/build.js +25 -6
- package/dist/commands/build.js.map +1 -1
- package/dist/commands/deprecate-qna.d.ts +1 -0
- package/dist/commands/deprecate-qna.js +33 -0
- package/dist/commands/deprecate-qna.js.map +1 -0
- package/dist/commands/log.js +2 -0
- package/dist/commands/log.js.map +1 -1
- package/dist/commands/new-helpers.d.ts +0 -1
- package/dist/commands/new-helpers.js +6 -3
- package/dist/commands/new-helpers.js.map +1 -1
- package/dist/commands/release.js +2 -0
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/start.js +90 -21
- package/dist/commands/start.js.map +1 -1
- package/dist/commands/status.js +6 -0
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/unanswered.d.ts +1 -0
- package/dist/commands/unanswered.js +25 -0
- package/dist/commands/unanswered.js.map +1 -0
- package/dist/commands/watch.d.ts +15 -0
- package/dist/commands/watch.js +153 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/index.js +44 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/autolink.d.ts +5 -0
- package/dist/lib/autolink.js +36 -2
- package/dist/lib/autolink.js.map +1 -1
- package/dist/lib/build-helpers.d.ts +10 -1
- package/dist/lib/build-helpers.js +27 -4
- package/dist/lib/build-helpers.js.map +1 -1
- package/dist/lib/build-steps.js +2 -1
- package/dist/lib/build-steps.js.map +1 -1
- package/dist/lib/config.d.ts +16 -0
- package/dist/lib/config.js +17 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/custom-card-renderer.js +2 -1
- package/dist/lib/custom-card-renderer.js.map +1 -1
- package/dist/lib/entry-href.d.ts +7 -0
- package/dist/lib/entry-href.js +8 -0
- package/dist/lib/entry-href.js.map +1 -0
- package/dist/lib/markdown-processors.d.ts +2 -1
- package/dist/lib/post-template.js +3 -3
- package/dist/lib/post-template.js.map +1 -1
- package/dist/lib/template-helpers.d.ts +2 -1
- package/dist/lib/template-helpers.js +32 -2
- package/dist/lib/template-helpers.js.map +1 -1
- package/dist/lib/template-types.d.ts +14 -0
- package/dist/lib/templates.js +14 -4
- package/dist/lib/templates.js.map +1 -1
- package/dist/linters/index.js +2 -0
- package/dist/linters/index.js.map +1 -1
- package/dist/linters/jira-prefix.js +32 -16
- package/dist/linters/jira-prefix.js.map +1 -1
- package/dist/linters/qna.d.ts +3 -0
- package/dist/linters/qna.js +120 -0
- package/dist/linters/qna.js.map +1 -0
- package/dist/templates/about-watch.md +29 -0
- package/dist/templates/hub.css +677 -0
- package/dist/templates/hub.js +743 -0
- package/dist/templates/qna.md +11 -0
- package/dist/templates/steer.txt +2 -0
- package/dist/templates/styles.css +53 -0
- package/dist/utils/id.d.ts +1 -1
- package/dist/utils/id.js +4 -1
- package/dist/utils/id.js.map +1 -1
- package/dist/utils/qna.d.ts +46 -0
- package/dist/utils/qna.js +157 -0
- package/dist/utils/qna.js.map +1 -0
- package/dist/watch/__tests__/git-utils.test.d.ts +1 -0
- package/dist/watch/__tests__/git-utils.test.js +17 -0
- package/dist/watch/__tests__/git-utils.test.js.map +1 -0
- package/dist/watch/collector.d.ts +14 -0
- package/dist/watch/collector.js +33 -0
- package/dist/watch/collector.js.map +1 -0
- package/dist/watch/collectors/acli.d.ts +15 -0
- package/dist/watch/collectors/acli.js +197 -0
- package/dist/watch/collectors/acli.js.map +1 -0
- package/dist/watch/collectors/entries.d.ts +9 -0
- package/dist/watch/collectors/entries.js +90 -0
- package/dist/watch/collectors/entries.js.map +1 -0
- package/dist/watch/collectors/git.d.ts +12 -0
- package/dist/watch/collectors/git.js +58 -0
- package/dist/watch/collectors/git.js.map +1 -0
- package/dist/watch/collectors/glab.d.ts +10 -0
- package/dist/watch/collectors/glab.js +273 -0
- package/dist/watch/collectors/glab.js.map +1 -0
- package/dist/watch/collectors/index.d.ts +10 -0
- package/dist/watch/collectors/index.js +21 -0
- package/dist/watch/collectors/index.js.map +1 -0
- package/dist/watch/collectors/jira.d.ts +3 -0
- package/dist/watch/collectors/jira.js +111 -0
- package/dist/watch/collectors/jira.js.map +1 -0
- package/dist/watch/collectors/type-guards.d.ts +17 -0
- package/dist/watch/collectors/type-guards.js +35 -0
- package/dist/watch/collectors/type-guards.js.map +1 -0
- package/dist/watch/collectors/types.d.ts +37 -0
- package/dist/watch/collectors/types.js +9 -0
- package/dist/watch/collectors/types.js.map +1 -0
- package/dist/watch/dashboard-html.d.ts +11 -0
- package/dist/watch/dashboard-html.js +81 -0
- package/dist/watch/dashboard-html.js.map +1 -0
- package/dist/watch/dashboard-script.d.ts +12 -0
- package/dist/watch/dashboard-script.js +20 -0
- package/dist/watch/dashboard-script.js.map +1 -0
- package/dist/watch/dashboard-styles.d.ts +12 -0
- package/dist/watch/dashboard-styles.js +20 -0
- package/dist/watch/dashboard-styles.js.map +1 -0
- package/dist/watch/dashboard.d.ts +12 -0
- package/dist/watch/dashboard.js +50 -0
- package/dist/watch/dashboard.js.map +1 -0
- package/dist/watch/git-utils.d.ts +36 -0
- package/dist/watch/git-utils.js +117 -0
- package/dist/watch/git-utils.js.map +1 -0
- package/dist/watch/hub-state.d.ts +39 -0
- package/dist/watch/hub-state.js +57 -0
- package/dist/watch/hub-state.js.map +1 -0
- package/dist/watch/hub.d.ts +2 -0
- package/dist/watch/hub.js +252 -0
- package/dist/watch/hub.js.map +1 -0
- package/dist/watch/log-utils.d.ts +13 -0
- package/dist/watch/log-utils.js +24 -0
- package/dist/watch/log-utils.js.map +1 -0
- package/dist/watch/notify.d.ts +18 -0
- package/dist/watch/notify.js +45 -0
- package/dist/watch/notify.js.map +1 -0
- package/dist/watch/state.d.ts +38 -0
- package/dist/watch/state.js +36 -0
- package/dist/watch/state.js.map +1 -0
- package/dist/watch/strategies/behind-default.d.ts +5 -0
- package/dist/watch/strategies/behind-default.js +21 -0
- package/dist/watch/strategies/behind-default.js.map +1 -0
- package/dist/watch/strategies/idle.d.ts +5 -0
- package/dist/watch/strategies/idle.js +21 -0
- package/dist/watch/strategies/idle.js.map +1 -0
- package/dist/watch/strategies/in-progress.d.ts +5 -0
- package/dist/watch/strategies/in-progress.js +21 -0
- package/dist/watch/strategies/in-progress.js.map +1 -0
- package/dist/watch/strategies/index.d.ts +34 -0
- package/dist/watch/strategies/index.js +148 -0
- package/dist/watch/strategies/index.js.map +1 -0
- package/dist/watch/strategies/jira-not-in-review.d.ts +2 -0
- package/dist/watch/strategies/jira-not-in-review.js +29 -0
- package/dist/watch/strategies/jira-not-in-review.js.map +1 -0
- package/dist/watch/strategies/jira-still-open.d.ts +2 -0
- package/dist/watch/strategies/jira-still-open.js +31 -0
- package/dist/watch/strategies/jira-still-open.js.map +1 -0
- package/dist/watch/strategies/mr-approval-optional.d.ts +5 -0
- package/dist/watch/strategies/mr-approval-optional.js +21 -0
- package/dist/watch/strategies/mr-approval-optional.js.map +1 -0
- package/dist/watch/strategies/mr-approved.d.ts +5 -0
- package/dist/watch/strategies/mr-approved.js +21 -0
- package/dist/watch/strategies/mr-approved.js.map +1 -0
- package/dist/watch/strategies/mr-conflicts.d.ts +5 -0
- package/dist/watch/strategies/mr-conflicts.js +21 -0
- package/dist/watch/strategies/mr-conflicts.js.map +1 -0
- package/dist/watch/strategies/mr-draft.d.ts +5 -0
- package/dist/watch/strategies/mr-draft.js +21 -0
- package/dist/watch/strategies/mr-draft.js.map +1 -0
- package/dist/watch/strategies/mr-open-comments.d.ts +5 -0
- package/dist/watch/strategies/mr-open-comments.js +21 -0
- package/dist/watch/strategies/mr-open-comments.js.map +1 -0
- package/dist/watch/strategies/narrative-missing.d.ts +5 -0
- package/dist/watch/strategies/narrative-missing.js +21 -0
- package/dist/watch/strategies/narrative-missing.js.map +1 -0
- package/dist/watch/strategies/no-mr.d.ts +5 -0
- package/dist/watch/strategies/no-mr.js +21 -0
- package/dist/watch/strategies/no-mr.js.map +1 -0
- package/dist/watch/strategies/pipeline-failed.d.ts +5 -0
- package/dist/watch/strategies/pipeline-failed.js +21 -0
- package/dist/watch/strategies/pipeline-failed.js.map +1 -0
- package/dist/watch/strategies/pipeline-running.d.ts +5 -0
- package/dist/watch/strategies/pipeline-running.js +21 -0
- package/dist/watch/strategies/pipeline-running.js.map +1 -0
- package/dist/watch/strategies/ready-for-release.d.ts +7 -0
- package/dist/watch/strategies/ready-for-release.js +35 -0
- package/dist/watch/strategies/ready-for-release.js.map +1 -0
- package/dist/watch/strategies/ready-to-commit.d.ts +5 -0
- package/dist/watch/strategies/ready-to-commit.js +21 -0
- package/dist/watch/strategies/ready-to-commit.js.map +1 -0
- package/dist/watch/strategies/ready-to-push.d.ts +5 -0
- package/dist/watch/strategies/ready-to-push.js +21 -0
- package/dist/watch/strategies/ready-to-push.js.map +1 -0
- package/dist/watch/strategies/released.d.ts +6 -0
- package/dist/watch/strategies/released.js +21 -0
- package/dist/watch/strategies/released.js.map +1 -0
- package/dist/watch/strategies/squash-merged.d.ts +6 -0
- package/dist/watch/strategies/squash-merged.js +21 -0
- package/dist/watch/strategies/squash-merged.js.map +1 -0
- package/dist/watch/strategies/types.d.ts +31 -0
- package/dist/watch/strategies/types.js +12 -0
- package/dist/watch/strategies/types.js.map +1 -0
- package/dist/watch/strategies/unstaged-changes.d.ts +5 -0
- package/dist/watch/strategies/unstaged-changes.js +21 -0
- package/dist/watch/strategies/unstaged-changes.js.map +1 -0
- package/dist/watch/strategies/wrong-branch.d.ts +6 -0
- package/dist/watch/strategies/wrong-branch.js +26 -0
- package/dist/watch/strategies/wrong-branch.js.map +1 -0
- package/dist/watch/types.d.ts +211 -0
- package/dist/watch/types.js +25 -0
- package/dist/watch/types.js.map +1 -0
- package/dist/watch/watcher-fs.d.ts +39 -0
- package/dist/watch/watcher-fs.js +105 -0
- package/dist/watch/watcher-fs.js.map +1 -0
- package/dist/watch/watcher-http.d.ts +19 -0
- package/dist/watch/watcher-http.js +47 -0
- package/dist/watch/watcher-http.js.map +1 -0
- package/dist/watch/watcher.d.ts +4 -0
- package/dist/watch/watcher.js +185 -0
- package/dist/watch/watcher.js.map +1 -0
- package/package.json +8 -2
- package/src/templates/about-watch.md +29 -0
- package/src/templates/hub.css +677 -0
- package/src/templates/hub.js +743 -0
- package/src/templates/qna.md +11 -0
- package/src/templates/steer.txt +2 -0
- package/src/templates/styles.css +53 -0
package/dist/templates/steer.txt
CHANGED
|
@@ -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
|
+
}
|
package/dist/utils/id.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/** Parse a ticket ID string like "LB-123" or "
|
|
1
|
+
/** Parse a ticket ID string like "LB-123", "#45", or "#45-slug" into prefix and number. */
|
|
2
2
|
export declare function parseTicketId(input: string): {
|
|
3
3
|
prefix: string;
|
|
4
4
|
number: number;
|
package/dist/utils/id.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
|
-
/** Parse a ticket ID string like "LB-123" or "
|
|
3
|
+
/** Parse a ticket ID string like "LB-123", "#45", or "#45-slug" into prefix and number. */
|
|
4
4
|
export function parseTicketId(input) {
|
|
5
|
+
if (/^#\d+(?:-.*)?$/.test(input)) {
|
|
6
|
+
return { prefix: '#', number: parseInt(input.replace(/^#/, ''), 10) };
|
|
7
|
+
}
|
|
5
8
|
const match = input.match(/^([A-Za-z]+)-(\d+)(?:-.*)?$/);
|
|
6
9
|
if (match) {
|
|
7
10
|
return { prefix: match[1].toUpperCase(), number: parseInt(match[2], 10) };
|
package/dist/utils/id.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,
|
|
1
|
+
{"version":3,"file":"id.js","sourceRoot":"","sources":["../../src/utils/id.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,2FAA2F;AAC3F,MAAM,UAAU,aAAa,CAAC,KAAa;IACzC,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IACxE,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACzD,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,MAAM,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC;IAC5E,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,uFAAuF;AACvF,MAAM,UAAU,SAAS,CAAC,UAAkB,EAAE,MAAc;IAC1D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,UAAU,CAAC;QAAE,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,CAAC;IAEnE,MAAM,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE;QACtD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,CAAC;YAC9C,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,IAAI,SAAS,GAAG,CAAC,CAAC;IAClB,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,IAAI,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC;QAC3E,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACnC,IAAI,GAAG,GAAG,SAAS,EAAE,CAAC;gBACpB,SAAS,GAAG,GAAG,CAAC;YAClB,CAAC;QACH,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,SAAS,GAAG,CAAC,CAAC;IACjC,OAAO,GAAG,MAAM,CAAC,WAAW,EAAE,IAAI,UAAU,EAAE,CAAC;AACjD,CAAC"}
|
|
@@ -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>;
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import fs from 'fs-extra';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import matter from 'gray-matter';
|
|
4
|
+
function isQnaStatus(value) {
|
|
5
|
+
return value === 'asked' || value === 'answered' || value === 'deprecated';
|
|
6
|
+
}
|
|
7
|
+
function parseNumericId(idInput) {
|
|
8
|
+
const id = parseInt(idInput, 10);
|
|
9
|
+
return isNaN(id) ? null : id;
|
|
10
|
+
}
|
|
11
|
+
export async function resolveQnaEntry(qnaDir, idInput) {
|
|
12
|
+
const id = parseNumericId(idInput);
|
|
13
|
+
if (id === null) {
|
|
14
|
+
return { error: `Invalid ID '${idInput}': must be a numeric value.` };
|
|
15
|
+
}
|
|
16
|
+
const found = await findQnaById(qnaDir, id);
|
|
17
|
+
if (!found) {
|
|
18
|
+
return { error: `QnA question #${id} not found.` };
|
|
19
|
+
}
|
|
20
|
+
return { id, item: found.item, filepath: found.filepath };
|
|
21
|
+
}
|
|
22
|
+
export function sortQnaItems(items) {
|
|
23
|
+
return [...items].sort((a, b) => {
|
|
24
|
+
if (a.status === 'asked' && b.status !== 'asked')
|
|
25
|
+
return -1;
|
|
26
|
+
if (a.status !== 'asked' && b.status === 'asked')
|
|
27
|
+
return 1;
|
|
28
|
+
const timeA = new Date(a.dateAsked).getTime() || 0;
|
|
29
|
+
const timeB = new Date(b.dateAsked).getTime() || 0;
|
|
30
|
+
if (timeA !== timeB)
|
|
31
|
+
return timeA - timeB;
|
|
32
|
+
return a.id - b.id;
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function slugifyQuestion(text, maxLen = 40) {
|
|
36
|
+
let slug = text
|
|
37
|
+
.toLowerCase()
|
|
38
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
39
|
+
.replace(/^-+|-+$/g, '');
|
|
40
|
+
if (slug.length > maxLen) {
|
|
41
|
+
slug = slug.substring(0, maxLen).replace(/-+$/, '');
|
|
42
|
+
}
|
|
43
|
+
return slug || 'question';
|
|
44
|
+
}
|
|
45
|
+
export function parseQnaContent(body) {
|
|
46
|
+
const questionMatch = body.match(/###\s+Question\s*\n([\s\S]*?)(?=###\s+Answer|$)/i);
|
|
47
|
+
const answerMatch = body.match(/###\s+Answer\s*\n([\s\S]*)$/i);
|
|
48
|
+
return {
|
|
49
|
+
question: questionMatch ? questionMatch[1].trim() : '',
|
|
50
|
+
answer: answerMatch ? answerMatch[1].trim() : '',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
export function formatQnaMarkdown(params) {
|
|
54
|
+
const lines = ['---', `id: ${params.id}`, `status: ${params.status}`, `dateAsked: ${params.dateAsked}`];
|
|
55
|
+
if (params.dateAnswered)
|
|
56
|
+
lines.push(`dateAnswered: ${params.dateAnswered}`);
|
|
57
|
+
if (params.dateDeprecated)
|
|
58
|
+
lines.push(`dateDeprecated: ${params.dateDeprecated}`);
|
|
59
|
+
lines.push('---', '', '### Question', params.question.trim(), '', '### Answer', (params.answer || '').trim(), '');
|
|
60
|
+
return lines.join('\n');
|
|
61
|
+
}
|
|
62
|
+
async function parseQnaFile(filepath) {
|
|
63
|
+
if (!(await fs.pathExists(filepath)))
|
|
64
|
+
return null;
|
|
65
|
+
const raw = await fs.readFile(filepath, 'utf8');
|
|
66
|
+
const { data, content } = matter(raw);
|
|
67
|
+
if (data.id === undefined)
|
|
68
|
+
return null;
|
|
69
|
+
const id = Number(data.id);
|
|
70
|
+
const status = isQnaStatus(data.status) ? data.status : 'asked';
|
|
71
|
+
let dateAsked = '';
|
|
72
|
+
if (data.dateAsked instanceof Date) {
|
|
73
|
+
dateAsked = data.dateAsked.toISOString().split('T')[0];
|
|
74
|
+
}
|
|
75
|
+
else if (typeof data.dateAsked === 'string') {
|
|
76
|
+
dateAsked = data.dateAsked.split('T')[0];
|
|
77
|
+
}
|
|
78
|
+
let dateAnswered;
|
|
79
|
+
if (data.dateAnswered instanceof Date) {
|
|
80
|
+
dateAnswered = data.dateAnswered.toISOString().split('T')[0];
|
|
81
|
+
}
|
|
82
|
+
else if (typeof data.dateAnswered === 'string') {
|
|
83
|
+
dateAnswered = data.dateAnswered.split('T')[0];
|
|
84
|
+
}
|
|
85
|
+
let dateDeprecated;
|
|
86
|
+
if (data.dateDeprecated instanceof Date) {
|
|
87
|
+
dateDeprecated = data.dateDeprecated.toISOString().split('T')[0];
|
|
88
|
+
}
|
|
89
|
+
else if (typeof data.dateDeprecated === 'string') {
|
|
90
|
+
dateDeprecated = data.dateDeprecated.split('T')[0];
|
|
91
|
+
}
|
|
92
|
+
const { question, answer } = parseQnaContent(content);
|
|
93
|
+
const filename = filepath.split('/').pop() || '';
|
|
94
|
+
return {
|
|
95
|
+
id,
|
|
96
|
+
status,
|
|
97
|
+
dateAsked,
|
|
98
|
+
dateAnswered,
|
|
99
|
+
dateDeprecated,
|
|
100
|
+
question,
|
|
101
|
+
answer,
|
|
102
|
+
filename,
|
|
103
|
+
filepath,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
export async function getAllQna(qnaDir) {
|
|
107
|
+
if (!(await fs.pathExists(qnaDir)))
|
|
108
|
+
return [];
|
|
109
|
+
const files = await fs.readdir(qnaDir);
|
|
110
|
+
const items = [];
|
|
111
|
+
for (const file of files) {
|
|
112
|
+
if (!file.endsWith('.md'))
|
|
113
|
+
continue;
|
|
114
|
+
const filepath = join(qnaDir, file);
|
|
115
|
+
if ((await fs.stat(filepath)).isDirectory())
|
|
116
|
+
continue;
|
|
117
|
+
const item = await parseQnaFile(filepath);
|
|
118
|
+
if (item)
|
|
119
|
+
items.push(item);
|
|
120
|
+
}
|
|
121
|
+
return items;
|
|
122
|
+
}
|
|
123
|
+
export async function getNextQnaId(qnaDir) {
|
|
124
|
+
if (!(await fs.pathExists(qnaDir)))
|
|
125
|
+
return 1;
|
|
126
|
+
const files = await fs.readdir(qnaDir);
|
|
127
|
+
let maxId = 0;
|
|
128
|
+
for (const file of files) {
|
|
129
|
+
if (!file.endsWith('.md'))
|
|
130
|
+
continue;
|
|
131
|
+
const m = file.match(/^(\d+)-/);
|
|
132
|
+
if (m) {
|
|
133
|
+
const num = parseInt(m[1], 10);
|
|
134
|
+
if (num > maxId)
|
|
135
|
+
maxId = num;
|
|
136
|
+
}
|
|
137
|
+
// Also try frontmatter if needed
|
|
138
|
+
const filepath = join(qnaDir, file);
|
|
139
|
+
try {
|
|
140
|
+
const item = await parseQnaFile(filepath);
|
|
141
|
+
if (item && item.id > maxId)
|
|
142
|
+
maxId = item.id;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// ignore
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return maxId + 1;
|
|
149
|
+
}
|
|
150
|
+
export async function findQnaById(qnaDir, id) {
|
|
151
|
+
const items = await getAllQna(qnaDir);
|
|
152
|
+
const match = items.find((q) => q.id === id);
|
|
153
|
+
if (!match)
|
|
154
|
+
return null;
|
|
155
|
+
return { item: match, filepath: match.filepath };
|
|
156
|
+
}
|
|
157
|
+
//# sourceMappingURL=qna.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qna.js","sourceRoot":"","sources":["../../src/utils/qna.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,UAAU,CAAC;AAC1B,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,aAAa,CAAC;AAkBjC,SAAS,WAAW,CAAC,KAAc;IACjC,OAAO,KAAK,KAAK,OAAO,IAAI,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,YAAY,CAAC;AAC7E,CAAC;AAED,SAAS,cAAc,CAAC,OAAe;IACrC,MAAM,EAAE,GAAG,QAAQ,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;IACjC,OAAO,KAAK,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,OAAe;IAEf,MAAM,EAAE,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACnC,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC;QAChB,OAAO,EAAE,KAAK,EAAE,eAAe,OAAO,6BAA6B,EAAE,CAAC;IACxE,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,WAAW,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IAC5C,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,EAAE,KAAK,EAAE,iBAAiB,EAAE,aAAa,EAAE,CAAC;IACrD,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,YAAY,CAAiE,KAAU;IACrG,OAAO,CAAC,GAAG,KAAK,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE;QAC9B,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,CAAC,CAAC,CAAC;QAC5D,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO,IAAI,CAAC,CAAC,MAAM,KAAK,OAAO;YAAE,OAAO,CAAC,CAAC;QAC3D,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnD,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QACnD,IAAI,KAAK,KAAK,KAAK;YAAE,OAAO,KAAK,GAAG,KAAK,CAAC;QAC1C,OAAO,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,EAAE,CAAC;IACrB,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY,EAAE,SAAiB,EAAE;IAC/D,IAAI,IAAI,GAAG,IAAI;SACZ,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,IAAI,IAAI,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC;QACzB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,IAAI,IAAI,UAAU,CAAC;AAC5B,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,IAAY;IAC1C,MAAM,aAAa,GAAG,IAAI,CAAC,KAAK,CAAC,kDAAkD,CAAC,CAAC;IACrF,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;IAC/D,OAAO;QACL,QAAQ,EAAE,aAAa,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;QACtD,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE;KACjD,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAQjC;IACC,MAAM,KAAK,GAAa,CAAC,KAAK,EAAE,OAAO,MAAM,CAAC,EAAE,EAAE,EAAE,WAAW,MAAM,CAAC,MAAM,EAAE,EAAE,cAAc,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC;IAClH,IAAI,MAAM,CAAC,YAAY;QAAE,KAAK,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;IAC5E,IAAI,MAAM,CAAC,cAAc;QAAE,KAAK,CAAC,IAAI,CAAC,mBAAmB,MAAM,CAAC,cAAc,EAAE,CAAC,CAAC;IAClF,KAAK,CAAC,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,YAAY,EAAE,CAAC,MAAM,CAAC,MAAM,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IAClH,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,QAAgB;IAC1C,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAClD,MAAM,GAAG,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;IACtC,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3B,MAAM,MAAM,GAAc,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC;IAE3E,IAAI,SAAS,GAAG,EAAE,CAAC;IACnB,IAAI,IAAI,CAAC,SAAS,YAAY,IAAI,EAAE,CAAC;QACnC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACzD,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,EAAE,CAAC;QAC9C,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3C,CAAC;IAED,IAAI,YAAgC,CAAC;IACrC,IAAI,IAAI,CAAC,YAAY,YAAY,IAAI,EAAE,CAAC;QACtC,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/D,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,YAAY,KAAK,QAAQ,EAAE,CAAC;QACjD,YAAY,GAAG,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACjD,CAAC;IAED,IAAI,cAAkC,CAAC;IACvC,IAAI,IAAI,CAAC,cAAc,YAAY,IAAI,EAAE,CAAC;QACxC,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,CAAC;SAAM,IAAI,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ,EAAE,CAAC;QACnD,cAAc,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,eAAe,CAAC,OAAO,CAAC,CAAC;IACtD,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC;IAEjD,OAAO;QACL,EAAE;QACF,MAAM;QACN,SAAS;QACT,YAAY;QACZ,cAAc;QACd,QAAQ;QACR,MAAM;QACN,QAAQ;QACR,QAAQ;KACT,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC5C,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAC9C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,MAAM,KAAK,GAAc,EAAE,CAAC;IAE5B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,EAAE;YAAE,SAAS;QACtD,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC1C,IAAI,IAAI;YAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC7B,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc;IAC/C,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC;YAAE,SAAS;QACpC,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAChC,IAAI,CAAC,EAAE,CAAC;YACN,MAAM,GAAG,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YAC/B,IAAI,GAAG,GAAG,KAAK;gBAAE,KAAK,GAAG,GAAG,CAAC;QAC/B,CAAC;QACD,iCAAiC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;QACpC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,MAAM,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC1C,IAAI,IAAI,IAAI,IAAI,CAAC,EAAE,GAAG,KAAK;gBAAE,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC;QAC/C,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,KAAK,GAAG,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAc,EAAE,EAAU;IAC1D,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAC;IACtC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;IAC7C,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IACxB,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,CAAC;AACnD,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { extractTicketFromBranch } from '../git-utils.js';
|
|
3
|
+
describe('extractTicketFromBranch', () => {
|
|
4
|
+
it('extracts a Jira ticket by configured prefix', () => {
|
|
5
|
+
expect(extractTicketFromBranch('feat/LB-34-cool-thing', 'LB', undefined)).toBe('LB-34');
|
|
6
|
+
});
|
|
7
|
+
it('extracts a bare numeric GitLab work item when a gitlab prefix is set', () => {
|
|
8
|
+
expect(extractTicketFromBranch('feat/36-content-api-garbage-endpoints', 'LB', '#')).toBe('#36');
|
|
9
|
+
});
|
|
10
|
+
it('handles a literal #-prefixed GitLab branch segment', () => {
|
|
11
|
+
expect(extractTicketFromBranch('feat/#32-api-anbindung', 'LB', '#')).toBe('#32');
|
|
12
|
+
});
|
|
13
|
+
it('returns undefined when neither prefix matches', () => {
|
|
14
|
+
expect(extractTicketFromBranch('feat/36-thing', 'LB', undefined)).toBeUndefined();
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
//# sourceMappingURL=git-utils.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-utils.test.js","sourceRoot":"","sources":["../../../src/watch/__tests__/git-utils.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,uBAAuB,EAAE,MAAM,iBAAiB,CAAC;AAE1D,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;IACvC,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,uBAAuB,CAAC,uBAAuB,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC1F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,sEAAsE,EAAE,GAAG,EAAE;QAC9E,MAAM,CAAC,uBAAuB,CAAC,uCAAuC,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAClG,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,CAAC,uBAAuB,CAAC,wBAAwB,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACnF,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,GAAG,EAAE;QACvD,MAAM,CAAC,uBAAuB,CAAC,eAAe,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC,CAAC,aAAa,EAAE,CAAC;IACpF,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detail metrics collector for logbook watch.
|
|
3
|
+
*
|
|
4
|
+
* Provides the expanded detail view data (LOC, staged/unstaged, commits).
|
|
5
|
+
* Uses shared git-utils helpers to avoid duplication with the git collector.
|
|
6
|
+
*/
|
|
7
|
+
import type { ProjectDetails } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Collect detailed project metrics for the expandable detail view.
|
|
10
|
+
*
|
|
11
|
+
* @param cwd - Project root directory containing .project-logbook config.
|
|
12
|
+
* @returns The detailed metrics snapshot.
|
|
13
|
+
*/
|
|
14
|
+
export declare function collectDetails(cwd: string): Promise<ProjectDetails>;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detail metrics collector for logbook watch.
|
|
3
|
+
*
|
|
4
|
+
* Provides the expanded detail view data (LOC, staged/unstaged, commits).
|
|
5
|
+
* Uses shared git-utils helpers to avoid duplication with the git collector.
|
|
6
|
+
*/
|
|
7
|
+
import { getConfig, getLogbookDirPath } from '../lib/config.js';
|
|
8
|
+
import { getDefaultBranch, getChangedLOC } from '../lib/git-helpers.js';
|
|
9
|
+
import { parseGitStatus, commitsAheadOfRemote, commitsVsDefault } from './git-utils.js';
|
|
10
|
+
/**
|
|
11
|
+
* Collect detailed project metrics for the expandable detail view.
|
|
12
|
+
*
|
|
13
|
+
* @param cwd - Project root directory containing .project-logbook config.
|
|
14
|
+
* @returns The detailed metrics snapshot.
|
|
15
|
+
*/
|
|
16
|
+
export async function collectDetails(cwd) {
|
|
17
|
+
const config = getConfig(cwd);
|
|
18
|
+
const entriesDir = getLogbookDirPath(config, cwd);
|
|
19
|
+
const defaultBranch = getDefaultBranch(cwd);
|
|
20
|
+
const loc = await getChangedLOC(entriesDir, cwd);
|
|
21
|
+
const { staged, unstaged } = parseGitStatus(cwd);
|
|
22
|
+
return {
|
|
23
|
+
locChanged: loc.total,
|
|
24
|
+
insertions: loc.insertions,
|
|
25
|
+
deletions: loc.deletions,
|
|
26
|
+
unstagedFiles: unstaged,
|
|
27
|
+
stagedFiles: staged,
|
|
28
|
+
commitsAhead: commitsAheadOfRemote(cwd),
|
|
29
|
+
commitsOnBranch: commitsVsDefault(cwd, defaultBranch),
|
|
30
|
+
defaultBranch,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=collector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collector.js","sourceRoot":"","sources":["../../src/watch/collector.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,SAAS,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACxE,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAGxF;;;;;GAKG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,GAAW;IAC9C,MAAM,MAAM,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;IAC9B,MAAM,UAAU,GAAG,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,aAAa,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC5C,MAAM,GAAG,GAAG,MAAM,aAAa,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;IACjD,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;IAEjD,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,KAAK;QACrB,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,aAAa,EAAE,QAAQ;QACvB,WAAW,EAAE,MAAM;QACnB,YAAY,EAAE,oBAAoB,CAAC,GAAG,CAAC;QACvC,eAAe,EAAE,gBAAgB,CAAC,GAAG,EAAE,aAAa,CAAC;QACrD,aAAa;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acli collector for logbook watch.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Atlassian CLI (`acli`) tool to fetch Jira issues assigned to the
|
|
5
|
+
* current user. Runs on interval (60s) since it calls an external API.
|
|
6
|
+
*
|
|
7
|
+
* Auth sources (checked in order):
|
|
8
|
+
* 1. Shell env vars: JIRA_SITE, JIRA_EMAIL, JIRA_KEY
|
|
9
|
+
* 2. .env file in project root
|
|
10
|
+
* 3. Already-authenticated acli session
|
|
11
|
+
*
|
|
12
|
+
* Falls back gracefully when acli is not installed or auth is missing.
|
|
13
|
+
*/
|
|
14
|
+
import type { Collector } from './types.js';
|
|
15
|
+
export declare const acliCollector: Collector;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* acli collector for logbook watch.
|
|
3
|
+
*
|
|
4
|
+
* Uses the Atlassian CLI (`acli`) tool to fetch Jira issues assigned to the
|
|
5
|
+
* current user. Runs on interval (60s) since it calls an external API.
|
|
6
|
+
*
|
|
7
|
+
* Auth sources (checked in order):
|
|
8
|
+
* 1. Shell env vars: JIRA_SITE, JIRA_EMAIL, JIRA_KEY
|
|
9
|
+
* 2. .env file in project root
|
|
10
|
+
* 3. Already-authenticated acli session
|
|
11
|
+
*
|
|
12
|
+
* Falls back gracefully when acli is not installed or auth is missing.
|
|
13
|
+
*/
|
|
14
|
+
import { execSync, exec } from 'node:child_process';
|
|
15
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
+
import { promisify } from 'node:util';
|
|
17
|
+
import { join } from 'node:path';
|
|
18
|
+
import { getConfig, GITLAB_PREFIX } from '../../lib/config.js';
|
|
19
|
+
import { getCurrentBranch } from '../../lib/git-helpers.js';
|
|
20
|
+
import { extractTicketFromBranch } from '../git-utils.js';
|
|
21
|
+
/** Check if acli CLI is installed. */
|
|
22
|
+
function isAcliInstalled() {
|
|
23
|
+
try {
|
|
24
|
+
execSync('acli --version', {
|
|
25
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
26
|
+
});
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Check if acli is already authenticated. */
|
|
34
|
+
function isAcliAuthenticated() {
|
|
35
|
+
try {
|
|
36
|
+
execSync('acli jira auth status', {
|
|
37
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
38
|
+
});
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/** Parse .env file content, return matching vars. */
|
|
46
|
+
function parseEnvFile(cwd) {
|
|
47
|
+
const envPath = join(cwd, '.env');
|
|
48
|
+
if (!existsSync(envPath))
|
|
49
|
+
return {};
|
|
50
|
+
try {
|
|
51
|
+
const raw = readFileSync(envPath, 'utf8');
|
|
52
|
+
const result = {};
|
|
53
|
+
for (const line of raw.split('\n')) {
|
|
54
|
+
const trimmed = line.trim();
|
|
55
|
+
if (!trimmed || trimmed.startsWith('#'))
|
|
56
|
+
continue;
|
|
57
|
+
const eq = trimmed.indexOf('=');
|
|
58
|
+
if (eq === -1)
|
|
59
|
+
continue;
|
|
60
|
+
const key = trimmed.slice(0, eq).trim();
|
|
61
|
+
const val = trimmed
|
|
62
|
+
.slice(eq + 1)
|
|
63
|
+
.trim()
|
|
64
|
+
.replace(/^["']|["']$/g, '');
|
|
65
|
+
if (key === 'JIRA_SITE')
|
|
66
|
+
result.site = val;
|
|
67
|
+
else if (key === 'JIRA_EMAIL')
|
|
68
|
+
result.email = val;
|
|
69
|
+
else if (key === 'JIRA_KEY')
|
|
70
|
+
result.key = val;
|
|
71
|
+
}
|
|
72
|
+
return result;
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return {};
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
/** Resolve auth credentials from env vars, falling back to .env. */
|
|
79
|
+
function resolveAuth(cwd) {
|
|
80
|
+
const site = process.env.JIRA_SITE;
|
|
81
|
+
const email = process.env.JIRA_EMAIL;
|
|
82
|
+
const key = process.env.JIRA_KEY;
|
|
83
|
+
if (site && email && key) {
|
|
84
|
+
return { site, email, key };
|
|
85
|
+
}
|
|
86
|
+
const envFile = parseEnvFile(cwd);
|
|
87
|
+
if (envFile.site && envFile.email && envFile.key) {
|
|
88
|
+
return { site: envFile.site, email: envFile.email, key: envFile.key };
|
|
89
|
+
}
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
function loginWithEnv(auth) {
|
|
93
|
+
try {
|
|
94
|
+
execSync(`echo "${auth.key}" | acli jira auth login --site "${auth.site}" --email "${auth.email}" --token`, {
|
|
95
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
96
|
+
shell: '/bin/bash',
|
|
97
|
+
timeout: 15_000,
|
|
98
|
+
});
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Fetch real `updated` timestamps from Jira in parallel. */
|
|
106
|
+
async function fetchUpdatedTimestamps(keys) {
|
|
107
|
+
const results = new Map();
|
|
108
|
+
const run = promisify(exec);
|
|
109
|
+
const cmds = keys.map(async (key) => {
|
|
110
|
+
try {
|
|
111
|
+
const { stdout } = await run(`acli jira workitem view ${key} --fields 'updated' --json`, { timeout: 10_000 });
|
|
112
|
+
const data = JSON.parse(stdout);
|
|
113
|
+
if (data?.fields?.updated)
|
|
114
|
+
results.set(key, data.fields.updated);
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* skip */
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
await Promise.all(cmds);
|
|
121
|
+
return results;
|
|
122
|
+
}
|
|
123
|
+
export const acliCollector = {
|
|
124
|
+
name: 'acli',
|
|
125
|
+
schedule: { type: 'interval', ms: 60_000 },
|
|
126
|
+
async available(cwd) {
|
|
127
|
+
return isAcliInstalled() && (!!resolveAuth(cwd) || isAcliAuthenticated());
|
|
128
|
+
},
|
|
129
|
+
async collect(cwd, state) {
|
|
130
|
+
try {
|
|
131
|
+
const auth = resolveAuth(cwd);
|
|
132
|
+
if (auth && !isAcliAuthenticated())
|
|
133
|
+
loginWithEnv(auth);
|
|
134
|
+
if (!isAcliAuthenticated()) {
|
|
135
|
+
state.jiraAssignedIssues = [];
|
|
136
|
+
state.jiraStatus = undefined;
|
|
137
|
+
state.jiraLastUpdated = new Date().toISOString();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
let site = auth?.site ?? '';
|
|
141
|
+
if (!site) {
|
|
142
|
+
try {
|
|
143
|
+
const out = execSync('acli jira auth status --json 2>/dev/null || acli jira auth status', {
|
|
144
|
+
encoding: 'utf8',
|
|
145
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
146
|
+
timeout: 5_000,
|
|
147
|
+
});
|
|
148
|
+
const m = out.match(/Site:\s*(\S+)/);
|
|
149
|
+
if (m)
|
|
150
|
+
site = m[1];
|
|
151
|
+
}
|
|
152
|
+
catch { }
|
|
153
|
+
}
|
|
154
|
+
const baseUrl = site ? `https://${site}` : undefined;
|
|
155
|
+
const rawIssues = execSync(`acli jira workitem search --jql "assignee = currentUser() AND resolution = Unresolved" --json`, { encoding: 'utf8', cwd, stdio: ['ignore', 'pipe', 'ignore'], timeout: 15_000 }).trim();
|
|
156
|
+
if (!rawIssues) {
|
|
157
|
+
state.jiraAssignedIssues = [];
|
|
158
|
+
state.jiraLastUpdated = new Date().toISOString();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const issues = JSON.parse(rawIssues);
|
|
162
|
+
const updatedMap = await fetchUpdatedTimestamps(issues.map((i) => i.key));
|
|
163
|
+
state.jiraAssignedIssues = issues.map((issue) => ({
|
|
164
|
+
key: issue.key,
|
|
165
|
+
summary: issue.fields.summary,
|
|
166
|
+
status: issue.fields.status.name,
|
|
167
|
+
url: baseUrl ? `${baseUrl}/browse/${issue.key}` : '',
|
|
168
|
+
jiraBaseUrl: baseUrl ?? '',
|
|
169
|
+
updated: updatedMap.get(issue.key) ?? new Date().toISOString(),
|
|
170
|
+
}));
|
|
171
|
+
const config = getConfig(cwd);
|
|
172
|
+
const ticketKey = state.activeTicketId ?? extractTicketFromBranch(getCurrentBranch(cwd), config.jiraPrefix, GITLAB_PREFIX);
|
|
173
|
+
if (ticketKey) {
|
|
174
|
+
try {
|
|
175
|
+
const issueOut = execSync(`acli jira workitem view ${ticketKey} --json`, {
|
|
176
|
+
encoding: 'utf8',
|
|
177
|
+
cwd,
|
|
178
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
179
|
+
timeout: 10_000,
|
|
180
|
+
}).trim();
|
|
181
|
+
if (issueOut) {
|
|
182
|
+
const issue = JSON.parse(issueOut);
|
|
183
|
+
state.jiraStatus = issue.fields.status.name;
|
|
184
|
+
}
|
|
185
|
+
else
|
|
186
|
+
state.jiraStatus = undefined;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
state.jiraStatus = undefined;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
state.jiraLastUpdated = new Date().toISOString();
|
|
193
|
+
}
|
|
194
|
+
catch { }
|
|
195
|
+
},
|
|
196
|
+
};
|
|
197
|
+
//# sourceMappingURL=acli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"acli.js","sourceRoot":"","sources":["../../../src/watch/collectors/acli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Logbook entries collector for logbook watch.
|
|
3
|
+
*
|
|
4
|
+
* Reads the active lockfile, checks for narrative placeholders, and detects
|
|
5
|
+
* released entries. Runs on file-system events (instant feedback when the
|
|
6
|
+
* lockfile or entry files change).
|
|
7
|
+
*/
|
|
8
|
+
import type { Collector } from './types.js';
|
|
9
|
+
export declare const entriesCollector: Collector;
|