auq-mcp-server 2.4.0 → 2.6.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 (32) hide show
  1. package/README.md +40 -0
  2. package/dist/bin/auq.js +40 -0
  3. package/dist/bin/tui-app.js +115 -1
  4. package/dist/package.json +1 -1
  5. package/dist/src/cli/commands/sessions.js +144 -2
  6. package/dist/src/cli/commands/update.js +124 -0
  7. package/dist/src/config/__tests__/updateCheck.test.js +34 -0
  8. package/dist/src/config/defaults.js +2 -0
  9. package/dist/src/config/types.js +2 -0
  10. package/dist/src/tui/components/Footer.js +4 -1
  11. package/dist/src/tui/components/Header.js +3 -1
  12. package/dist/src/tui/components/UpdateBadge.js +29 -0
  13. package/dist/src/tui/components/UpdateOverlay.js +199 -0
  14. package/dist/src/tui/constants/keybindings.js +3 -0
  15. package/dist/src/update/__tests__/cache.test.js +136 -0
  16. package/dist/src/update/__tests__/changelog.test.js +86 -0
  17. package/dist/src/update/__tests__/checker.test.js +148 -0
  18. package/dist/src/update/__tests__/index.test.js +37 -0
  19. package/dist/src/update/__tests__/installer.test.js +117 -0
  20. package/dist/src/update/__tests__/package-manager.test.js +73 -0
  21. package/dist/src/update/__tests__/version.test.js +74 -0
  22. package/dist/src/update/cache.js +74 -0
  23. package/dist/src/update/changelog.js +63 -0
  24. package/dist/src/update/checker.js +121 -0
  25. package/dist/src/update/index.js +15 -0
  26. package/dist/src/update/installer.js +51 -0
  27. package/dist/src/update/package-manager.js +49 -0
  28. package/dist/src/update/types.js +7 -0
  29. package/dist/src/update/version.js +114 -0
  30. package/package.json +1 -1
  31. package/dist/src/tui/components/Spinner.js +0 -19
  32. package/dist/src/tui/utils/__tests__/detectTheme.test.js +0 -78
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Package manager detection for the auto-update system.
3
+ *
4
+ * Detects which package manager was used to install AUQ by inspecting
5
+ * environment variables and process paths, following a priority-based
6
+ * detection strategy.
7
+ */
8
+ /** The npm package name used for global install/update commands. */
9
+ export const PACKAGE_NAME = "auq-mcp-server";
10
+ /**
11
+ * Detects which package manager was used to install AUQ.
12
+ *
13
+ * Detection priority:
14
+ * 1. `process.env.npm_config_user_agent` — most reliable for npm/yarn/pnpm
15
+ * 2. `process.env.npm_execpath` — fallback for npm-based managers
16
+ * 3. `process.execPath` — check if executable path contains package manager names
17
+ * 4. Default to npm as universal fallback
18
+ *
19
+ * @returns Package manager info with name and global install command prefix.
20
+ * The install command does NOT include the package name — the caller appends it.
21
+ */
22
+ export function detectPackageManager() {
23
+ // Priority 1: npm_config_user_agent (most reliable)
24
+ // Format is typically: "npm/10.2.0 node/v20.9.0 darwin arm64"
25
+ const userAgent = process.env.npm_config_user_agent || "";
26
+ if (userAgent.includes("bun"))
27
+ return { name: "bun", installCommand: "bun add -g" };
28
+ if (userAgent.includes("yarn"))
29
+ return { name: "yarn", installCommand: "yarn global add" };
30
+ if (userAgent.includes("pnpm"))
31
+ return { name: "pnpm", installCommand: "pnpm add -g" };
32
+ if (userAgent.includes("npm"))
33
+ return { name: "npm", installCommand: "npm install -g" };
34
+ // Priority 2: npm_execpath (path to the package manager executable)
35
+ const execpath = (process.env.npm_execpath || "").toLowerCase();
36
+ if (execpath.includes("bun"))
37
+ return { name: "bun", installCommand: "bun add -g" };
38
+ if (execpath.includes("yarn"))
39
+ return { name: "yarn", installCommand: "yarn global add" };
40
+ if (execpath.includes("pnpm"))
41
+ return { name: "pnpm", installCommand: "pnpm add -g" };
42
+ // Priority 3: process.execPath (runtime executable path)
43
+ // Useful for detecting bun when run outside a package manager context
44
+ const execPath = process.execPath.toLowerCase();
45
+ if (execPath.includes("bun"))
46
+ return { name: "bun", installCommand: "bun add -g" };
47
+ // Priority 4: Default fallback — npm is universally available
48
+ return { name: "npm", installCommand: "npm install -g" };
49
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * TypeScript type definitions for the auto-update system.
3
+ *
4
+ * These interfaces define the data structures used throughout the update module
5
+ * for version checking, caching, package manager detection, and changelog fetching.
6
+ */
7
+ export {};
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Semver comparison utilities for the auto-update system.
3
+ *
4
+ * Provides version parsing, comparison, and update type detection
5
+ * without requiring external dependencies like the `semver` package.
6
+ */
7
+ import { readFileSync } from "fs";
8
+ import { dirname, join } from "path";
9
+ import { fileURLToPath } from "url";
10
+ /**
11
+ * Parse a version string into its numeric components.
12
+ *
13
+ * Strips a leading `v` prefix if present. Splits prerelease
14
+ * identifiers on `-`. Handles two-part versions like "2.5"
15
+ * by treating the missing patch as 0.
16
+ *
17
+ * @param version - Semver string, e.g., "2.5.0", "v1.2.3-beta.1"
18
+ * @returns Parsed version object
19
+ * @throws Error if any numeric component is NaN
20
+ */
21
+ export function parseVersion(version) {
22
+ // Strip leading 'v' prefix
23
+ const cleaned = version.startsWith("v") ? version.slice(1) : version;
24
+ // Separate prerelease from numeric parts
25
+ const [numericPart, ...prereleaseParts] = cleaned.split("-");
26
+ const prerelease = prereleaseParts.length > 0 ? prereleaseParts.join("-") : undefined;
27
+ const parts = numericPart.split(".");
28
+ const major = Number(parts[0]);
29
+ const minor = Number(parts[1] ?? "0");
30
+ const patch = Number(parts[2] ?? "0");
31
+ if (Number.isNaN(major) || Number.isNaN(minor) || Number.isNaN(patch)) {
32
+ throw new Error(`Invalid version string: "${version}"`);
33
+ }
34
+ return { major, minor, patch, prerelease };
35
+ }
36
+ /**
37
+ * Determine if `latest` is newer than `current`.
38
+ *
39
+ * Compares major, minor, and patch components numerically.
40
+ * For prerelease handling: a version WITH a prerelease tag is
41
+ * considered OLDER than the same version WITHOUT one
42
+ * (e.g., `2.5.0-beta.1 < 2.5.0`).
43
+ *
44
+ * @param current - Currently installed version
45
+ * @param latest - Latest available version
46
+ * @returns true if `latest` is strictly newer than `current`
47
+ */
48
+ export function isNewer(current, latest) {
49
+ const c = parseVersion(current);
50
+ const l = parseVersion(latest);
51
+ // Compare major.minor.patch numerically
52
+ if (l.major !== c.major)
53
+ return l.major > c.major;
54
+ if (l.minor !== c.minor)
55
+ return l.minor > c.minor;
56
+ if (l.patch !== c.patch)
57
+ return l.patch > c.patch;
58
+ // Same numeric version — check prerelease
59
+ // A version with prerelease is older than the same version without
60
+ if (c.prerelease && !l.prerelease)
61
+ return true;
62
+ if (!c.prerelease && l.prerelease)
63
+ return false;
64
+ // Both have prerelease or both don't — considered equal
65
+ return false;
66
+ }
67
+ /**
68
+ * Determine the type of update between two versions.
69
+ *
70
+ * @param current - Currently installed version
71
+ * @param latest - Latest available version
72
+ * @returns "major" | "minor" | "patch"
73
+ */
74
+ export function getUpdateType(current, latest) {
75
+ const c = parseVersion(current);
76
+ const l = parseVersion(latest);
77
+ if (l.major > c.major)
78
+ return "major";
79
+ if (l.minor > c.minor)
80
+ return "minor";
81
+ return "patch";
82
+ }
83
+ /**
84
+ * Read the current installed version from the package's package.json.
85
+ *
86
+ * Resolves the path relative to this module's location, trying
87
+ * multiple paths to handle both local dev and global install layouts.
88
+ *
89
+ * @returns The current version string
90
+ * @throws Error if package.json cannot be found or parsed
91
+ */
92
+ export function getCurrentVersion() {
93
+ const __filename = fileURLToPath(import.meta.url);
94
+ const __dirname = dirname(__filename);
95
+ // Try different possible paths for package.json
96
+ // - ../../package.json: from src/update/ in local dev (src/update -> src -> root)
97
+ // - ../../../package.json: from dist/update/ in global install (dist/update -> dist -> root)
98
+ const possiblePaths = [
99
+ join(__dirname, "..", "..", "package.json"),
100
+ join(__dirname, "..", "..", "..", "package.json"),
101
+ ];
102
+ for (const path of possiblePaths) {
103
+ try {
104
+ const packageJson = JSON.parse(readFileSync(path, "utf-8"));
105
+ if (packageJson.version) {
106
+ return packageJson.version;
107
+ }
108
+ }
109
+ catch {
110
+ // Try next path
111
+ }
112
+ }
113
+ throw new Error("Could not determine current version from package.json");
114
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auq-mcp-server",
3
- "version": "2.4.0",
3
+ "version": "2.6.0",
4
4
  "main": "dist/index.js",
5
5
  "bin": {
6
6
  "auq": "dist/bin/auq.js"
@@ -1,19 +0,0 @@
1
- import { Text } from "ink";
2
- import React, { useEffect, useState } from "react";
3
- import { useTheme } from "../ThemeContext.js";
4
- const SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
5
- /**
6
- * Spinner displays an animated loading indicator
7
- * Uses braille pattern characters for smooth animation
8
- */
9
- export const Spinner = ({ color }) => {
10
- const { theme } = useTheme();
11
- const [frame, setFrame] = useState(0);
12
- useEffect(() => {
13
- const timer = setInterval(() => {
14
- setFrame((prev) => (prev + 1) % SPINNER_FRAMES.length);
15
- }, 80);
16
- return () => clearInterval(timer);
17
- }, []);
18
- return (React.createElement(Text, { color: color ?? theme.colors.primary }, SPINNER_FRAMES[frame]));
19
- };
@@ -1,78 +0,0 @@
1
- import { describe, it, expect, beforeEach, afterEach } from "vitest";
2
- import { detectSystemTheme, clearThemeCache } from "../detectTheme.js";
3
- describe("detectSystemTheme", () => {
4
- const originalEnv = process.env;
5
- beforeEach(() => {
6
- // Reset environment and clear cache before each test
7
- process.env = { ...originalEnv };
8
- clearThemeCache();
9
- });
10
- afterEach(() => {
11
- process.env = originalEnv;
12
- clearThemeCache();
13
- });
14
- describe("COLORFGBG detection", () => {
15
- it("should detect dark theme when background is 0 (black)", () => {
16
- process.env.COLORFGBG = "15;0";
17
- expect(detectSystemTheme()).toBe("dark");
18
- });
19
- it("should detect dark theme when background is 6", () => {
20
- process.env.COLORFGBG = "7;6";
21
- expect(detectSystemTheme()).toBe("dark");
22
- });
23
- it("should detect light theme when background is 7 (white/light gray)", () => {
24
- process.env.COLORFGBG = "0;7";
25
- expect(detectSystemTheme()).toBe("light");
26
- });
27
- it("should detect light theme when background is 15 (bright white)", () => {
28
- process.env.COLORFGBG = "0;15";
29
- expect(detectSystemTheme()).toBe("light");
30
- });
31
- it("should handle three-part COLORFGBG format", () => {
32
- // Some terminals use foreground;middle;background format
33
- process.env.COLORFGBG = "15;default;0";
34
- expect(detectSystemTheme()).toBe("dark");
35
- });
36
- it("should handle three-part COLORFGBG with light background", () => {
37
- process.env.COLORFGBG = "0;default;15";
38
- expect(detectSystemTheme()).toBe("light");
39
- });
40
- });
41
- describe("fallback behavior", () => {
42
- it("should fallback to dark when COLORFGBG is not set", () => {
43
- delete process.env.COLORFGBG;
44
- expect(detectSystemTheme()).toBe("dark");
45
- });
46
- it("should fallback to dark when COLORFGBG is invalid", () => {
47
- process.env.COLORFGBG = "invalid";
48
- expect(detectSystemTheme()).toBe("dark");
49
- });
50
- it("should fallback to dark when COLORFGBG has invalid number", () => {
51
- process.env.COLORFGBG = "15;abc";
52
- expect(detectSystemTheme()).toBe("dark");
53
- });
54
- it("should fallback to dark when COLORFGBG is empty", () => {
55
- process.env.COLORFGBG = "";
56
- expect(detectSystemTheme()).toBe("dark");
57
- });
58
- });
59
- describe("caching", () => {
60
- it("should cache the result", () => {
61
- process.env.COLORFGBG = "15;0";
62
- expect(detectSystemTheme()).toBe("dark");
63
- // Change the environment variable
64
- process.env.COLORFGBG = "0;15";
65
- // Should still return cached result
66
- expect(detectSystemTheme()).toBe("dark");
67
- });
68
- it("should return fresh result after cache is cleared", () => {
69
- process.env.COLORFGBG = "15;0";
70
- expect(detectSystemTheme()).toBe("dark");
71
- // Clear cache and change environment
72
- clearThemeCache();
73
- process.env.COLORFGBG = "0;15";
74
- // Should return new result
75
- expect(detectSystemTheme()).toBe("light");
76
- });
77
- });
78
- });