@akiojin/gwt 2.12.1 → 2.13.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 (76) hide show
  1. package/dist/cli/ui/components/App.d.ts.map +1 -1
  2. package/dist/cli/ui/components/App.js +72 -3
  3. package/dist/cli/ui/components/App.js.map +1 -1
  4. package/dist/cli/ui/components/screens/BranchListScreen.d.ts +3 -1
  5. package/dist/cli/ui/components/screens/BranchListScreen.d.ts.map +1 -1
  6. package/dist/cli/ui/components/screens/BranchListScreen.js +154 -32
  7. package/dist/cli/ui/components/screens/BranchListScreen.js.map +1 -1
  8. package/dist/cli/ui/hooks/useGitData.d.ts.map +1 -1
  9. package/dist/cli/ui/hooks/useGitData.js +17 -0
  10. package/dist/cli/ui/hooks/useGitData.js.map +1 -1
  11. package/dist/cli/ui/types.d.ts +2 -0
  12. package/dist/cli/ui/types.d.ts.map +1 -1
  13. package/dist/cli/ui/utils/branchFormatter.d.ts.map +1 -1
  14. package/dist/cli/ui/utils/branchFormatter.js +7 -2
  15. package/dist/cli/ui/utils/branchFormatter.js.map +1 -1
  16. package/dist/cli/ui/utils/modelOptions.d.ts.map +1 -1
  17. package/dist/cli/ui/utils/modelOptions.js +7 -0
  18. package/dist/cli/ui/utils/modelOptions.js.map +1 -1
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +6 -0
  21. package/dist/index.js.map +1 -1
  22. package/dist/logging/logger.d.ts +24 -0
  23. package/dist/logging/logger.d.ts.map +1 -0
  24. package/dist/logging/logger.js +57 -0
  25. package/dist/logging/logger.js.map +1 -0
  26. package/dist/logging/rotation.d.ts +6 -0
  27. package/dist/logging/rotation.d.ts.map +1 -0
  28. package/dist/logging/rotation.js +26 -0
  29. package/dist/logging/rotation.js.map +1 -0
  30. package/dist/web/server/index.d.ts.map +1 -1
  31. package/dist/web/server/index.js +3 -3
  32. package/dist/web/server/index.js.map +1 -1
  33. package/dist/web/server/routes/branches.d.ts +2 -2
  34. package/dist/web/server/routes/branches.d.ts.map +1 -1
  35. package/dist/web/server/routes/branches.js.map +1 -1
  36. package/dist/web/server/routes/config.d.ts +2 -2
  37. package/dist/web/server/routes/config.d.ts.map +1 -1
  38. package/dist/web/server/routes/config.js.map +1 -1
  39. package/dist/web/server/routes/index.d.ts +2 -2
  40. package/dist/web/server/routes/index.d.ts.map +1 -1
  41. package/dist/web/server/routes/index.js.map +1 -1
  42. package/dist/web/server/routes/sessions.d.ts +2 -2
  43. package/dist/web/server/routes/sessions.d.ts.map +1 -1
  44. package/dist/web/server/routes/sessions.js.map +1 -1
  45. package/dist/web/server/routes/worktrees.d.ts +2 -2
  46. package/dist/web/server/routes/worktrees.d.ts.map +1 -1
  47. package/dist/web/server/routes/worktrees.js.map +1 -1
  48. package/dist/web/server/types.d.ts +4 -0
  49. package/dist/web/server/types.d.ts.map +1 -0
  50. package/dist/web/server/types.js +2 -0
  51. package/dist/web/server/types.js.map +1 -0
  52. package/dist/worktree.d.ts +1 -0
  53. package/dist/worktree.d.ts.map +1 -1
  54. package/dist/worktree.js.map +1 -1
  55. package/package.json +3 -2
  56. package/src/cli/ui/__tests__/components/ModelSelectorScreen.initial.test.tsx +13 -13
  57. package/src/cli/ui/__tests__/components/screens/BranchListScreen.test.tsx +81 -33
  58. package/src/cli/ui/__tests__/performance/branchList.performance.test.tsx +7 -3
  59. package/src/cli/ui/components/App.tsx +88 -2
  60. package/src/cli/ui/components/screens/BranchListScreen.tsx +198 -32
  61. package/src/cli/ui/hooks/useGitData.ts +20 -0
  62. package/src/cli/ui/types.ts +3 -0
  63. package/src/cli/ui/utils/branchFormatter.ts +7 -2
  64. package/src/cli/ui/utils/modelOptions.test.ts +14 -0
  65. package/src/cli/ui/utils/modelOptions.ts +7 -0
  66. package/src/index.ts +7 -0
  67. package/src/logging/logger.ts +79 -0
  68. package/src/logging/rotation.ts +25 -0
  69. package/src/web/server/index.ts +6 -4
  70. package/src/web/server/routes/branches.ts +2 -2
  71. package/src/web/server/routes/config.ts +2 -2
  72. package/src/web/server/routes/index.ts +2 -2
  73. package/src/web/server/routes/sessions.ts +2 -2
  74. package/src/web/server/routes/worktrees.ts +2 -2
  75. package/src/web/server/types.ts +14 -0
  76. package/src/worktree.ts +1 -0
@@ -0,0 +1,79 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import os from "node:os";
4
+ import pino, { type LoggerOptions, type Logger } from "pino";
5
+ import { pruneOldLogs } from "./rotation.js";
6
+
7
+ type Category = "cli" | "server" | "worker" | string;
8
+
9
+ export interface LoggerConfig {
10
+ level?: string;
11
+ logDir?: string;
12
+ filename?: string;
13
+ category?: Category;
14
+ base?: Record<string, unknown>;
15
+ keepDays?: number;
16
+ /** For tests or sync writes use pino.destination sync */
17
+ sync?: boolean;
18
+ }
19
+
20
+ /**
21
+ * Create a pino logger with unified structure and category field.
22
+ * - Writes to a single file stream (no per-component files)
23
+ * - Adds `category` to each log record
24
+ * - Prunes files older than keepDays at startup
25
+ */
26
+ export function createLogger(config: LoggerConfig = {}): Logger {
27
+ const level = config.level ?? process.env.LOG_LEVEL ?? "info";
28
+ const cwdBase = path.basename(process.cwd()) || "workspace";
29
+ const defaultLogDir = path.join(os.homedir(), ".gwt", "logs", cwdBase);
30
+ const logDir = config.logDir ?? defaultLogDir;
31
+ const filename = config.filename ?? `${formatDate(new Date())}.jsonl`;
32
+ const category = config.category ?? "default";
33
+ const keepDays = config.keepDays ?? 7;
34
+
35
+ if (!fs.existsSync(logDir)) {
36
+ fs.mkdirSync(logDir, { recursive: true });
37
+ }
38
+
39
+ // Startup rotation
40
+ pruneOldLogs(logDir, keepDays);
41
+
42
+ const destination = path.join(logDir, filename);
43
+
44
+ const options: LoggerOptions = {
45
+ level,
46
+ base: {
47
+ category,
48
+ ...(config.base ?? {}),
49
+ },
50
+ timestamp: pino.stdTimeFunctions.isoTime,
51
+ };
52
+
53
+ if (config.sync) {
54
+ const destinationStream = pino.destination({ dest: destination, sync: true });
55
+ return pino(options, destinationStream);
56
+ }
57
+
58
+ const transport = pino.transport({
59
+ targets: [
60
+ {
61
+ target: "pino/file",
62
+ options: { destination, mkdir: true, append: true },
63
+ level,
64
+ },
65
+ ],
66
+ });
67
+
68
+ return pino(options, transport);
69
+ }
70
+
71
+ /** Convenience logger for quick use (category defaults to "default"). */
72
+ export const logger = createLogger();
73
+
74
+ export function formatDate(date: Date): string {
75
+ const year = date.getFullYear();
76
+ const month = String(date.getMonth() + 1).padStart(2, "0");
77
+ const day = String(date.getDate()).padStart(2, "0");
78
+ return `${year}-${month}-${day}`;
79
+ }
@@ -0,0 +1,25 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+
4
+ /**
5
+ * Delete log files older than `keepDays` from the given directory.
6
+ * This is called at startup to enforce 7-day retention without size limits.
7
+ */
8
+ export function pruneOldLogs(logDir: string, keepDays = 7): void {
9
+ if (!fs.existsSync(logDir)) return;
10
+
11
+ const cutoff = Date.now() - keepDays * 24 * 60 * 60 * 1000;
12
+
13
+ for (const entry of fs.readdirSync(logDir)) {
14
+ const full = path.join(logDir, entry);
15
+ try {
16
+ const stat = fs.statSync(full);
17
+ if (!stat.isFile()) continue;
18
+ if (stat.mtime.getTime() < cutoff) {
19
+ fs.unlinkSync(full);
20
+ }
21
+ } catch {
22
+ // Ignore individual file errors to avoid breaking startup
23
+ }
24
+ }
25
+ }
@@ -14,6 +14,8 @@ import { PTYManager } from "./pty/manager.js";
14
14
  import { WebSocketHandler } from "./websocket/handler.js";
15
15
  import { registerRoutes } from "./routes/index.js";
16
16
  import { importOsEnvIntoSharedConfig } from "./env/importer.js";
17
+ import { createLogger } from "../../logging/logger.js";
18
+ import type { WebFastifyInstance } from "./types.js";
17
19
 
18
20
  const __filename = fileURLToPath(import.meta.url);
19
21
  const __dirname = dirname(__filename);
@@ -22,10 +24,10 @@ const __dirname = dirname(__filename);
22
24
  * Webサーバーを起動
23
25
  */
24
26
  export async function startWebServer(): Promise<void> {
25
- const fastify = Fastify({
26
- logger: {
27
- level: process.env.LOG_LEVEL || "info",
28
- },
27
+ const serverLogger = createLogger({ category: "server" });
28
+
29
+ const fastify: WebFastifyInstance = Fastify({
30
+ loggerInstance: serverLogger,
29
31
  });
30
32
 
31
33
  // PTYマネージャーとWebSocketハンドラーを初期化
@@ -4,7 +4,6 @@
4
4
  * ブランチ関連のREST APIエンドポイント。
5
5
  */
6
6
 
7
- import type { FastifyInstance } from "fastify";
8
7
  import {
9
8
  listBranches,
10
9
  getBranchByName,
@@ -16,12 +15,13 @@ import type {
16
15
  BranchSyncRequest,
17
16
  BranchSyncResult,
18
17
  } from "../../../types/api.js";
18
+ import type { WebFastifyInstance } from "../types.js";
19
19
 
20
20
  /**
21
21
  * ブランチ関連のルートを登録
22
22
  */
23
23
  export async function registerBranchRoutes(
24
- fastify: FastifyInstance,
24
+ fastify: WebFastifyInstance,
25
25
  ): Promise<void> {
26
26
  // GET /api/branches - すべてのブランチ一覧を取得
27
27
  fastify.get<{ Reply: ApiResponse<Branch[]> }>(
@@ -2,7 +2,6 @@
2
2
  * Config Routes
3
3
  */
4
4
 
5
- import type { FastifyInstance } from "fastify";
6
5
  import { loadToolsConfig, saveToolsConfig } from "../../../config/tools.js";
7
6
  import {
8
7
  loadEnvHistory,
@@ -17,6 +16,7 @@ import type {
17
16
  } from "../../../types/api.js";
18
17
  import type { CustomAITool as FileCustomAITool } from "../../../types/tools.js";
19
18
  import { getImportedEnvKeys } from "../env/importer.js";
19
+ import type { WebFastifyInstance } from "../types.js";
20
20
 
21
21
  function normalizeEnv(
22
22
  env: Record<string, string> | undefined,
@@ -135,7 +135,7 @@ function diffEnvHistory(
135
135
  }
136
136
 
137
137
  export async function registerConfigRoutes(
138
- fastify: FastifyInstance,
138
+ fastify: WebFastifyInstance,
139
139
  ): Promise<void> {
140
140
  fastify.get<{ Reply: ApiResponse<ConfigPayload> }>(
141
141
  "/api/config",
@@ -5,19 +5,19 @@
5
5
  * 仕様: specs/SPEC-d5e56259/contracts/rest-api.yaml
6
6
  */
7
7
 
8
- import type { FastifyInstance } from "fastify";
9
8
  import type { PTYManager } from "../pty/manager.js";
10
9
  import { registerBranchRoutes } from "./branches.js";
11
10
  import { registerWorktreeRoutes } from "./worktrees.js";
12
11
  import { registerSessionRoutes } from "./sessions.js";
13
12
  import { registerConfigRoutes } from "./config.js";
14
13
  import type { HealthResponse } from "../../../types/api.js";
14
+ import type { WebFastifyInstance } from "../types.js";
15
15
 
16
16
  /**
17
17
  * すべてのルートを登録
18
18
  */
19
19
  export async function registerRoutes(
20
- fastify: FastifyInstance,
20
+ fastify: WebFastifyInstance,
21
21
  ptyManager: PTYManager,
22
22
  ): Promise<void> {
23
23
  // ヘルスチェック
@@ -4,7 +4,6 @@
4
4
  * AI Toolセッション関連のREST APIエンドポイント。
5
5
  */
6
6
 
7
- import type { FastifyInstance } from "fastify";
8
7
  import type { PTYManager } from "../pty/manager.js";
9
8
  import type {
10
9
  ApiResponse,
@@ -13,12 +12,13 @@ import type {
13
12
  } from "../../../types/api.js";
14
13
  import { saveSession } from "../../../config/index.js";
15
14
  import { execa } from "execa";
15
+ import type { WebFastifyInstance } from "../types.js";
16
16
 
17
17
  /**
18
18
  * セッション関連のルートを登録
19
19
  */
20
20
  export async function registerSessionRoutes(
21
- fastify: FastifyInstance,
21
+ fastify: WebFastifyInstance,
22
22
  ptyManager: PTYManager,
23
23
  ): Promise<void> {
24
24
  // GET /api/sessions - すべてのセッション一覧を取得
@@ -4,7 +4,6 @@
4
4
  * Worktree関連のREST APIエンドポイント。
5
5
  */
6
6
 
7
- import type { FastifyInstance } from "fastify";
8
7
  import {
9
8
  listWorktrees,
10
9
  getWorktreeByPath,
@@ -16,12 +15,13 @@ import type {
16
15
  Worktree,
17
16
  CreateWorktreeRequest,
18
17
  } from "../../../types/api.js";
18
+ import type { WebFastifyInstance } from "../types.js";
19
19
 
20
20
  /**
21
21
  * Worktree関連のルートを登録
22
22
  */
23
23
  export async function registerWorktreeRoutes(
24
- fastify: FastifyInstance,
24
+ fastify: WebFastifyInstance,
25
25
  ): Promise<void> {
26
26
  // GET /api/worktrees - すべてのWorktree一覧を取得
27
27
  fastify.get<{ Reply: ApiResponse<Worktree[]> }>(
@@ -0,0 +1,14 @@
1
+ import type {
2
+ FastifyInstance,
3
+ RawReplyDefaultExpression,
4
+ RawRequestDefaultExpression,
5
+ RawServerDefault,
6
+ } from "fastify";
7
+ import type { Logger } from "pino";
8
+
9
+ export type WebFastifyInstance = FastifyInstance<
10
+ RawServerDefault,
11
+ RawRequestDefaultExpression<RawServerDefault>,
12
+ RawReplyDefaultExpression<RawServerDefault>,
13
+ Logger
14
+ >;
package/src/worktree.ts CHANGED
@@ -110,6 +110,7 @@ export interface WorktreeInfo {
110
110
  head: string;
111
111
  isAccessible?: boolean;
112
112
  invalidReason?: string;
113
+ hasUncommittedChanges?: boolean;
113
114
  }
114
115
 
115
116
  async function listWorktrees(): Promise<WorktreeInfo[]> {