auq-mcp-server 2.1.0 → 2.2.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 (35) hide show
  1. package/dist/bin/tui-app.js +199 -18
  2. package/dist/package.json +1 -1
  3. package/dist/src/i18n/locales/en.js +3 -0
  4. package/dist/src/i18n/locales/ko.js +3 -0
  5. package/dist/src/tui/components/Footer.js +6 -1
  6. package/dist/src/tui/components/OptionsList.js +81 -9
  7. package/dist/src/tui/components/QuestionDisplay.js +5 -9
  8. package/dist/src/tui/components/SessionDots.js +65 -0
  9. package/dist/src/tui/components/SessionPicker.js +159 -0
  10. package/dist/src/tui/components/StepperView.js +74 -9
  11. package/dist/src/tui/components/__tests__/SessionDots.test.js +92 -0
  12. package/dist/src/tui/components/__tests__/SessionPicker.test.js +125 -0
  13. package/dist/src/tui/components/__tests__/StepperView.state.test.js +101 -0
  14. package/dist/src/tui/themes/catppuccin-latte.js +18 -0
  15. package/dist/src/tui/themes/catppuccin-mocha.js +18 -0
  16. package/dist/src/tui/themes/dark.js +18 -0
  17. package/dist/src/tui/themes/dracula.js +18 -0
  18. package/dist/src/tui/themes/github-dark.js +18 -0
  19. package/dist/src/tui/themes/github-light.js +18 -0
  20. package/dist/src/tui/themes/gruvbox-dark.js +18 -0
  21. package/dist/src/tui/themes/gruvbox-light.js +18 -0
  22. package/dist/src/tui/themes/light.js +18 -0
  23. package/dist/src/tui/themes/monokai.js +18 -0
  24. package/dist/src/tui/themes/nord.js +18 -0
  25. package/dist/src/tui/themes/one-dark.js +18 -0
  26. package/dist/src/tui/themes/rose-pine.js +18 -0
  27. package/dist/src/tui/themes/solarized-dark.js +18 -0
  28. package/dist/src/tui/themes/solarized-light.js +18 -0
  29. package/dist/src/tui/themes/tokyo-night.js +18 -0
  30. package/dist/src/tui/types.js +1 -0
  31. package/dist/src/tui/utils/__tests__/relativeTime.test.js +31 -0
  32. package/dist/src/tui/utils/__tests__/sessionSwitching.test.js +82 -0
  33. package/dist/src/tui/utils/relativeTime.js +24 -0
  34. package/dist/src/tui/utils/sessionSwitching.js +56 -0
  35. package/package.json +1 -1
@@ -99,5 +99,23 @@ export const darkTheme = {
99
99
  codeBlockText: "#E7EEF5",
100
100
  codeBlockBorder: "#2A3238",
101
101
  },
102
+ sessionDots: {
103
+ active: "#46D9FF",
104
+ answered: "#5AF78E",
105
+ inProgress: "#FFD36A",
106
+ untouched: "#A0AAB4",
107
+ number: "#E7EEF5",
108
+ activeNumber: "#46D9FF",
109
+ },
110
+ sessionPicker: {
111
+ border: "#46D9FF",
112
+ title: "#46D9FF",
113
+ rowText: "#E7EEF5",
114
+ rowDim: "#A0AAB4",
115
+ highlightBg: "#0F2417",
116
+ highlightFg: "#5AF78E",
117
+ activeMark: "#46D9FF",
118
+ progress: "#46D9FF",
119
+ },
102
120
  },
103
121
  };
@@ -98,5 +98,23 @@ export const draculaTheme = {
98
98
  codeBlockText: "#f8f8f2",
99
99
  codeBlockBorder: "#44475a",
100
100
  },
101
+ sessionDots: {
102
+ active: "#bd93f9",
103
+ answered: "#50fa7b",
104
+ inProgress: "#f1fa8c",
105
+ untouched: "#7C8BBE",
106
+ number: "#f8f8f2",
107
+ activeNumber: "#bd93f9",
108
+ },
109
+ sessionPicker: {
110
+ border: "#bd93f9",
111
+ title: "#bd93f9",
112
+ rowText: "#f8f8f2",
113
+ rowDim: "#7C8BBE",
114
+ highlightBg: "#44475a",
115
+ highlightFg: "#50fa7b",
116
+ activeMark: "#bd93f9",
117
+ progress: "#8be9fd",
118
+ },
101
119
  },
102
120
  };
@@ -97,5 +97,23 @@ export const githubDarkTheme = {
97
97
  codeBlockText: "#c9d1d9",
98
98
  codeBlockBorder: "#30363d",
99
99
  },
100
+ sessionDots: {
101
+ active: "#58a6ff",
102
+ answered: "#3fb950",
103
+ inProgress: "#d29922",
104
+ untouched: "#a0a8b4",
105
+ number: "#c9d1d9",
106
+ activeNumber: "#58a6ff",
107
+ },
108
+ sessionPicker: {
109
+ border: "#58a6ff",
110
+ title: "#58a6ff",
111
+ rowText: "#c9d1d9",
112
+ rowDim: "#a0a8b4",
113
+ highlightBg: "#0d1117",
114
+ highlightFg: "#3fb950",
115
+ activeMark: "#58a6ff",
116
+ progress: "#58a6ff",
117
+ },
100
118
  },
101
119
  };
@@ -97,5 +97,23 @@ export const githubLightTheme = {
97
97
  codeBlockText: "#24292F",
98
98
  codeBlockBorder: "#D0D7DE",
99
99
  },
100
+ sessionDots: {
101
+ active: "#0969DA",
102
+ answered: "#1A7F37",
103
+ inProgress: "#9A6700",
104
+ untouched: "#6E7781",
105
+ number: "#24292F",
106
+ activeNumber: "#0969DA",
107
+ },
108
+ sessionPicker: {
109
+ border: "#0969DA",
110
+ title: "#0969DA",
111
+ rowText: "#24292F",
112
+ rowDim: "#6E7781",
113
+ highlightBg: "#DAFBE1",
114
+ highlightFg: "#1A7F37",
115
+ activeMark: "#0969DA",
116
+ progress: "#0969DA",
117
+ },
100
118
  },
101
119
  };
@@ -98,5 +98,23 @@ export const gruvboxDarkTheme = {
98
98
  codeBlockText: "#ebdbb2",
99
99
  codeBlockBorder: "#3c3836",
100
100
  },
101
+ sessionDots: {
102
+ active: "#458588",
103
+ answered: "#98971a",
104
+ inProgress: "#d79921",
105
+ untouched: "#a89984",
106
+ number: "#ebdbb2",
107
+ activeNumber: "#458588",
108
+ },
109
+ sessionPicker: {
110
+ border: "#458588",
111
+ title: "#458588",
112
+ rowText: "#ebdbb2",
113
+ rowDim: "#a89984",
114
+ highlightBg: "#3c3836",
115
+ highlightFg: "#98971a",
116
+ activeMark: "#458588",
117
+ progress: "#458588",
118
+ },
101
119
  },
102
120
  };
@@ -98,5 +98,23 @@ export const gruvboxLightTheme = {
98
98
  codeBlockText: "#3c3836",
99
99
  codeBlockBorder: "#d5c4a1",
100
100
  },
101
+ sessionDots: {
102
+ active: "#076678",
103
+ answered: "#79740e",
104
+ inProgress: "#b57614",
105
+ untouched: "#a89984",
106
+ number: "#3c3836",
107
+ activeNumber: "#076678",
108
+ },
109
+ sessionPicker: {
110
+ border: "#076678",
111
+ title: "#076678",
112
+ rowText: "#3c3836",
113
+ rowDim: "#a89984",
114
+ highlightBg: "#ebdbb2",
115
+ highlightFg: "#79740e",
116
+ activeMark: "#076678",
117
+ progress: "#076678",
118
+ },
101
119
  },
102
120
  };
@@ -98,5 +98,23 @@ export const lightTheme = {
98
98
  codeBlockText: "#24292F",
99
99
  codeBlockBorder: "#D0D7DE",
100
100
  },
101
+ sessionDots: {
102
+ active: "#007EA7",
103
+ answered: "#2DA44E",
104
+ inProgress: "#B07D00",
105
+ untouched: "#6E7781",
106
+ number: "#24292F",
107
+ activeNumber: "#007EA7",
108
+ },
109
+ sessionPicker: {
110
+ border: "#007EA7",
111
+ title: "#007EA7",
112
+ rowText: "#24292F",
113
+ rowDim: "#6E7781",
114
+ highlightBg: "#E6F9EE",
115
+ highlightFg: "#2DA44E",
116
+ activeMark: "#007EA7",
117
+ progress: "#007EA7",
118
+ },
101
119
  },
102
120
  };
@@ -99,5 +99,23 @@ export const monokaiTheme = {
99
99
  codeBlockText: "#F8F8F2",
100
100
  codeBlockBorder: "#49483E",
101
101
  },
102
+ sessionDots: {
103
+ active: "#66D9EF",
104
+ answered: "#A6E22E",
105
+ inProgress: "#E6DB74",
106
+ untouched: "#908B78",
107
+ number: "#F8F8F2",
108
+ activeNumber: "#66D9EF",
109
+ },
110
+ sessionPicker: {
111
+ border: "#66D9EF",
112
+ title: "#66D9EF",
113
+ rowText: "#F8F8F2",
114
+ rowDim: "#908B78",
115
+ highlightBg: "#383830",
116
+ highlightFg: "#A6E22E",
117
+ activeMark: "#66D9EF",
118
+ progress: "#66D9EF",
119
+ },
102
120
  },
103
121
  };
@@ -98,5 +98,23 @@ export const nordTheme = {
98
98
  codeBlockText: "#eceff4",
99
99
  codeBlockBorder: "#3b4252",
100
100
  },
101
+ sessionDots: {
102
+ active: "#88c0d0",
103
+ answered: "#a3be8c",
104
+ inProgress: "#ebcb8b",
105
+ untouched: "#616E88",
106
+ number: "#eceff4",
107
+ activeNumber: "#88c0d0",
108
+ },
109
+ sessionPicker: {
110
+ border: "#88c0d0",
111
+ title: "#88c0d0",
112
+ rowText: "#eceff4",
113
+ rowDim: "#616E88",
114
+ highlightBg: "#3b4252",
115
+ highlightFg: "#a3be8c",
116
+ activeMark: "#88c0d0",
117
+ progress: "#81a1c1",
118
+ },
101
119
  },
102
120
  };
@@ -98,5 +98,23 @@ export const oneDarkTheme = {
98
98
  codeBlockText: "#abb2bf",
99
99
  codeBlockBorder: "#3e4451",
100
100
  },
101
+ sessionDots: {
102
+ active: "#61afef",
103
+ answered: "#98c379",
104
+ inProgress: "#d19a66",
105
+ untouched: "#767D8A",
106
+ number: "#abb2bf",
107
+ activeNumber: "#61afef",
108
+ },
109
+ sessionPicker: {
110
+ border: "#61afef",
111
+ title: "#61afef",
112
+ rowText: "#abb2bf",
113
+ rowDim: "#767D8A",
114
+ highlightBg: "#3e4451",
115
+ highlightFg: "#98c379",
116
+ activeMark: "#61afef",
117
+ progress: "#56b6c2",
118
+ },
101
119
  },
102
120
  };
@@ -99,5 +99,23 @@ export const rosePineTheme = {
99
99
  codeBlockText: "#e0def4",
100
100
  codeBlockBorder: "#26233a",
101
101
  },
102
+ sessionDots: {
103
+ active: "#ebbcba",
104
+ answered: "#31748f",
105
+ inProgress: "#f6c177",
106
+ untouched: "#8884a0",
107
+ number: "#e0def4",
108
+ activeNumber: "#ebbcba",
109
+ },
110
+ sessionPicker: {
111
+ border: "#ebbcba",
112
+ title: "#ebbcba",
113
+ rowText: "#e0def4",
114
+ rowDim: "#8884a0",
115
+ highlightBg: "#191724",
116
+ highlightFg: "#31748f",
117
+ activeMark: "#ebbcba",
118
+ progress: "#9ccfd8",
119
+ },
102
120
  },
103
121
  };
@@ -98,5 +98,23 @@ export const solarizedDarkTheme = {
98
98
  codeBlockText: "#839496",
99
99
  codeBlockBorder: "#073642",
100
100
  },
101
+ sessionDots: {
102
+ active: "#268bd2",
103
+ answered: "#859900",
104
+ inProgress: "#b58900",
105
+ untouched: "#6c7c83",
106
+ number: "#839496",
107
+ activeNumber: "#268bd2",
108
+ },
109
+ sessionPicker: {
110
+ border: "#268bd2",
111
+ title: "#268bd2",
112
+ rowText: "#839496",
113
+ rowDim: "#6c7c83",
114
+ highlightBg: "#073642",
115
+ highlightFg: "#859900",
116
+ activeMark: "#268bd2",
117
+ progress: "#2aa198",
118
+ },
101
119
  },
102
120
  };
@@ -98,5 +98,23 @@ export const solarizedLightTheme = {
98
98
  codeBlockText: "#657b83",
99
99
  codeBlockBorder: "#eee8d5",
100
100
  },
101
+ sessionDots: {
102
+ active: "#268bd2",
103
+ answered: "#859900",
104
+ inProgress: "#b58900",
105
+ untouched: "#a3b1b1",
106
+ number: "#657b83",
107
+ activeNumber: "#268bd2",
108
+ },
109
+ sessionPicker: {
110
+ border: "#268bd2",
111
+ title: "#268bd2",
112
+ rowText: "#657b83",
113
+ rowDim: "#a3b1b1",
114
+ highlightBg: "#eee8d5",
115
+ highlightFg: "#859900",
116
+ activeMark: "#268bd2",
117
+ progress: "#2aa198",
118
+ },
101
119
  },
102
120
  };
@@ -98,5 +98,23 @@ export const tokyoNightTheme = {
98
98
  codeBlockText: "#c0caf5",
99
99
  codeBlockBorder: "#24283b",
100
100
  },
101
+ sessionDots: {
102
+ active: "#7aa2f7",
103
+ answered: "#9ece6a",
104
+ inProgress: "#e0af68",
105
+ untouched: "#7078A3",
106
+ number: "#c0caf5",
107
+ activeNumber: "#7aa2f7",
108
+ },
109
+ sessionPicker: {
110
+ border: "#7aa2f7",
111
+ title: "#7aa2f7",
112
+ rowText: "#c0caf5",
113
+ rowDim: "#7078A3",
114
+ highlightBg: "#24283b",
115
+ highlightFg: "#9ece6a",
116
+ activeMark: "#7aa2f7",
117
+ progress: "#7dcfff",
118
+ },
101
119
  },
102
120
  };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
+ import { formatRelativeTime } from "../relativeTime.js";
3
+ describe("formatRelativeTime", () => {
4
+ const now = new Date("2026-01-01T00:00:00.000Z");
5
+ beforeEach(() => {
6
+ vi.useFakeTimers();
7
+ vi.setSystemTime(now);
8
+ });
9
+ afterEach(() => {
10
+ vi.useRealTimers();
11
+ });
12
+ it("returns just now when timestamp is less than 5 seconds ago", () => {
13
+ const input = new Date(now.getTime() - 4000);
14
+ expect(formatRelativeTime(input)).toBe("just now");
15
+ });
16
+ it("formats 30 seconds ago as seconds", () => {
17
+ expect(formatRelativeTime(now.getTime() - 30_000)).toBe("30s ago");
18
+ });
19
+ it("formats 90 seconds ago as 1 minute", () => {
20
+ expect(formatRelativeTime(now.getTime() - 90_000)).toBe("1m ago");
21
+ });
22
+ it("formats 5 minutes ago as minutes", () => {
23
+ expect(formatRelativeTime(now.getTime() - 5 * 60_000)).toBe("5m ago");
24
+ });
25
+ it("formats 2 hours ago as hours", () => {
26
+ expect(formatRelativeTime(now.getTime() - 2 * 60 * 60_000)).toBe("2h ago");
27
+ });
28
+ it("formats 3 days ago as days", () => {
29
+ expect(formatRelativeTime(now.getTime() - 3 * 24 * 60 * 60_000)).toBe("3d ago");
30
+ });
31
+ });
@@ -0,0 +1,82 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { getAdjustedIndexAfterRemoval, getDirectJumpIndex, getNextSessionIndex, getPrevSessionIndex, } from "../sessionSwitching.js";
3
+ describe("getNextSessionIndex", () => {
4
+ it("returns current index when queue length is 0", () => {
5
+ expect(getNextSessionIndex(0, 0)).toBe(0);
6
+ });
7
+ it("returns current index when queue length is 1", () => {
8
+ expect(getNextSessionIndex(0, 1)).toBe(0);
9
+ });
10
+ it("advances from index 0 to 1 in queue length 2", () => {
11
+ expect(getNextSessionIndex(0, 2)).toBe(1);
12
+ });
13
+ it("wraps from index 1 to 0 in queue length 2", () => {
14
+ expect(getNextSessionIndex(1, 2)).toBe(0);
15
+ });
16
+ it("wraps from index 4 to 0 in queue length 5", () => {
17
+ expect(getNextSessionIndex(4, 5)).toBe(0);
18
+ });
19
+ it("advances from index 2 to 3 in queue length 5", () => {
20
+ expect(getNextSessionIndex(2, 5)).toBe(3);
21
+ });
22
+ });
23
+ describe("getPrevSessionIndex", () => {
24
+ it("returns current index when queue length is 0", () => {
25
+ expect(getPrevSessionIndex(0, 0)).toBe(0);
26
+ });
27
+ it("returns current index when queue length is 1", () => {
28
+ expect(getPrevSessionIndex(0, 1)).toBe(0);
29
+ });
30
+ it("wraps from index 0 to 1 in queue length 2", () => {
31
+ expect(getPrevSessionIndex(0, 2)).toBe(1);
32
+ });
33
+ it("moves from index 1 to 0 in queue length 2", () => {
34
+ expect(getPrevSessionIndex(1, 2)).toBe(0);
35
+ });
36
+ it("wraps from index 0 to 4 in queue length 5", () => {
37
+ expect(getPrevSessionIndex(0, 5)).toBe(4);
38
+ });
39
+ it("moves from index 3 to 2 in queue length 5", () => {
40
+ expect(getPrevSessionIndex(3, 5)).toBe(2);
41
+ });
42
+ });
43
+ describe("getDirectJumpIndex", () => {
44
+ it("maps keyNumber 1 to index 0", () => {
45
+ expect(getDirectJumpIndex(1, 2, 3)).toBe(0);
46
+ });
47
+ it("maps keyNumber 3 to index 2", () => {
48
+ expect(getDirectJumpIndex(3, 0, 3)).toBe(2);
49
+ });
50
+ it("returns null when keyNumber is out of queue range", () => {
51
+ expect(getDirectJumpIndex(4, 0, 3)).toBeNull();
52
+ });
53
+ it("returns null for keyNumber 0", () => {
54
+ expect(getDirectJumpIndex(0, 0, 3)).toBeNull();
55
+ });
56
+ it("returns null for keyNumber 10", () => {
57
+ expect(getDirectJumpIndex(10, 0, 12)).toBeNull();
58
+ });
59
+ it("returns null when key maps to current index", () => {
60
+ expect(getDirectJumpIndex(2, 1, 3)).toBeNull();
61
+ });
62
+ it("maps keyNumber 9 to index 8 when queue length is 9", () => {
63
+ expect(getDirectJumpIndex(9, 0, 9)).toBe(8);
64
+ });
65
+ });
66
+ describe("getAdjustedIndexAfterRemoval", () => {
67
+ it("decrements active index when removed index is before active", () => {
68
+ expect(getAdjustedIndexAfterRemoval(1, 3, 4)).toBe(2);
69
+ });
70
+ it("keeps active index when removed index is after active", () => {
71
+ expect(getAdjustedIndexAfterRemoval(4, 1, 4)).toBe(1);
72
+ });
73
+ it("keeps same index when active session is removed and next item slides in", () => {
74
+ expect(getAdjustedIndexAfterRemoval(1, 1, 3)).toBe(1);
75
+ });
76
+ it("clamps to last index when active removed and it was last item", () => {
77
+ expect(getAdjustedIndexAfterRemoval(3, 3, 3)).toBe(2);
78
+ });
79
+ it("returns 0 when queue becomes empty", () => {
80
+ expect(getAdjustedIndexAfterRemoval(0, 0, 0)).toBe(0);
81
+ });
82
+ });
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Format a timestamp as a concise relative time string.
3
+ *
4
+ * @param date - A Date object or a numeric epoch-ms timestamp
5
+ * @returns Human-readable label such as "just now", "12s ago", "3m ago"
6
+ */
7
+ export function formatRelativeTime(date) {
8
+ const now = Date.now();
9
+ const then = typeof date === "number" ? date : date.getTime();
10
+ const diffMs = Math.max(0, now - then);
11
+ const diffSec = Math.floor(diffMs / 1000);
12
+ if (diffSec < 5)
13
+ return "just now";
14
+ if (diffSec < 60)
15
+ return `${diffSec}s ago`;
16
+ const diffMin = Math.floor(diffSec / 60);
17
+ if (diffMin < 60)
18
+ return `${diffMin}m ago`;
19
+ const diffHr = Math.floor(diffMin / 60);
20
+ if (diffHr < 24)
21
+ return `${diffHr}h ago`;
22
+ const diffDay = Math.floor(diffHr / 24);
23
+ return `${diffDay}d ago`;
24
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Calculate the next session index with cyclic wrap-around.
3
+ * Returns current index if queue has <= 1 item.
4
+ */
5
+ export function getNextSessionIndex(currentIndex, queueLength) {
6
+ if (queueLength <= 1) {
7
+ return currentIndex;
8
+ }
9
+ return (currentIndex + 1) % queueLength;
10
+ }
11
+ /**
12
+ * Calculate the previous session index with cyclic wrap-around.
13
+ * Returns current index if queue has <= 1 item.
14
+ */
15
+ export function getPrevSessionIndex(currentIndex, queueLength) {
16
+ if (queueLength <= 1) {
17
+ return currentIndex;
18
+ }
19
+ return (currentIndex - 1 + queueLength) % queueLength;
20
+ }
21
+ /**
22
+ * Validate and return the target index for a direct jump (1-based input).
23
+ * Returns null if the jump is invalid (out of range, same as current).
24
+ */
25
+ export function getDirectJumpIndex(keyNumber, currentIndex, queueLength) {
26
+ if (keyNumber < 1 || keyNumber > 9) {
27
+ return null;
28
+ }
29
+ const targetIndex = keyNumber - 1;
30
+ if (targetIndex >= queueLength) {
31
+ return null;
32
+ }
33
+ if (targetIndex === currentIndex) {
34
+ return null;
35
+ }
36
+ return targetIndex;
37
+ }
38
+ /**
39
+ * Calculate the new active session index after removing a session.
40
+ * Handles: removal before active, removal at active, removal after active, queue becoming empty.
41
+ */
42
+ export function getAdjustedIndexAfterRemoval(removedIndex, activeIndex, newQueueLength) {
43
+ if (newQueueLength === 0) {
44
+ return 0;
45
+ }
46
+ if (removedIndex < activeIndex) {
47
+ return activeIndex - 1;
48
+ }
49
+ if (removedIndex > activeIndex) {
50
+ return activeIndex;
51
+ }
52
+ if (removedIndex < newQueueLength) {
53
+ return removedIndex;
54
+ }
55
+ return newQueueLength - 1;
56
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auq-mcp-server",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "auq": "dist/bin/auq.js"