@camaradesuk/git-worktree-tools 1.8.0 → 1.10.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 +48 -27
- package/dist/cli/cleanpr.js +74 -53
- package/dist/cli/cleanpr.js.map +1 -1
- package/dist/cli/cleanpr.test.js +2 -0
- package/dist/cli/cleanpr.test.js.map +1 -1
- package/dist/cli/lswt.js +32 -56
- package/dist/cli/lswt.js.map +1 -1
- package/dist/cli/lswt.test.js +17 -27
- package/dist/cli/lswt.test.js.map +1 -1
- package/dist/cli/newpr.d.ts +13 -1
- package/dist/cli/newpr.d.ts.map +1 -1
- package/dist/cli/newpr.js +350 -151
- package/dist/cli/newpr.js.map +1 -1
- package/dist/cli/newpr.test.js +314 -5
- package/dist/cli/newpr.test.js.map +1 -1
- package/dist/cli/prs.d.ts +3 -10
- package/dist/cli/prs.d.ts.map +1 -1
- package/dist/cli/prs.js +6 -168
- package/dist/cli/prs.js.map +1 -1
- package/dist/cli/prs.test.js +55 -0
- package/dist/cli/prs.test.js.map +1 -1
- package/dist/cli/wt/clean.d.ts +6 -2
- package/dist/cli/wt/clean.d.ts.map +1 -1
- package/dist/cli/wt/clean.js +401 -20
- package/dist/cli/wt/clean.js.map +1 -1
- package/dist/cli/wt/clean.test.d.ts +8 -0
- package/dist/cli/wt/clean.test.d.ts.map +1 -0
- package/dist/cli/wt/clean.test.js +624 -0
- package/dist/cli/wt/clean.test.js.map +1 -0
- package/dist/cli/wt/completion.d.ts +3 -0
- package/dist/cli/wt/completion.d.ts.map +1 -1
- package/dist/cli/wt/completion.js +80 -9
- package/dist/cli/wt/completion.js.map +1 -1
- package/dist/cli/wt/completion.test.js +102 -0
- package/dist/cli/wt/completion.test.js.map +1 -1
- package/dist/cli/wt/config.d.ts +3 -1
- package/dist/cli/wt/config.d.ts.map +1 -1
- package/dist/cli/wt/config.js +323 -32
- package/dist/cli/wt/config.js.map +1 -1
- package/dist/cli/wt/config.test.d.ts +2 -0
- package/dist/cli/wt/config.test.d.ts.map +1 -1
- package/dist/cli/wt/config.test.js +206 -26
- package/dist/cli/wt/config.test.js.map +1 -1
- package/dist/cli/wt/interactive-menu.d.ts +2 -0
- package/dist/cli/wt/interactive-menu.d.ts.map +1 -1
- package/dist/cli/wt/interactive-menu.js +346 -73
- package/dist/cli/wt/interactive-menu.js.map +1 -1
- package/dist/cli/wt/interactive-menu.test.d.ts +4 -2
- package/dist/cli/wt/interactive-menu.test.d.ts.map +1 -1
- package/dist/cli/wt/interactive-menu.test.js +383 -323
- package/dist/cli/wt/interactive-menu.test.js.map +1 -1
- package/dist/cli/wt/link.d.ts +3 -1
- package/dist/cli/wt/link.d.ts.map +1 -1
- package/dist/cli/wt/link.js +125 -38
- package/dist/cli/wt/link.js.map +1 -1
- package/dist/cli/wt/list.d.ts +4 -1
- package/dist/cli/wt/list.d.ts.map +1 -1
- package/dist/cli/wt/list.js +85 -16
- package/dist/cli/wt/list.js.map +1 -1
- package/dist/cli/wt/list.test.d.ts +10 -0
- package/dist/cli/wt/list.test.d.ts.map +1 -0
- package/dist/cli/wt/list.test.js +157 -0
- package/dist/cli/wt/list.test.js.map +1 -0
- package/dist/cli/wt/new.d.ts +8 -2
- package/dist/cli/wt/new.d.ts.map +1 -1
- package/dist/cli/wt/new.js +91 -46
- package/dist/cli/wt/new.js.map +1 -1
- package/dist/cli/wt/prs.d.ts +2 -1
- package/dist/cli/wt/prs.d.ts.map +1 -1
- package/dist/cli/wt/prs.js +3 -164
- package/dist/cli/wt/prs.js.map +1 -1
- package/dist/cli/wt/run-command.d.ts +4 -2
- package/dist/cli/wt/run-command.d.ts.map +1 -1
- package/dist/cli/wt/run-command.js +6 -4
- package/dist/cli/wt/run-command.js.map +1 -1
- package/dist/cli/wt/state.d.ts +3 -1
- package/dist/cli/wt/state.d.ts.map +1 -1
- package/dist/cli/wt/state.js +74 -10
- package/dist/cli/wt/state.js.map +1 -1
- package/dist/cli/wt/state.test.d.ts +9 -0
- package/dist/cli/wt/state.test.d.ts.map +1 -0
- package/dist/cli/wt/state.test.js +127 -0
- package/dist/cli/wt/state.test.js.map +1 -0
- package/dist/cli/wt/wt.test.d.ts +2 -2
- package/dist/cli/wt/wt.test.js +430 -212
- package/dist/cli/wt/wt.test.js.map +1 -1
- package/dist/cli/wt.d.ts.map +1 -1
- package/dist/cli/wt.js +50 -36
- package/dist/cli/wt.js.map +1 -1
- package/dist/cli/wt.unit.test.js +16 -38
- package/dist/cli/wt.unit.test.js.map +1 -1
- package/dist/cli/wtconfig.d.ts +1 -0
- package/dist/cli/wtconfig.d.ts.map +1 -1
- package/dist/cli/wtconfig.js +213 -21
- package/dist/cli/wtconfig.js.map +1 -1
- package/dist/cli/wtconfig.test.js +3 -0
- package/dist/cli/wtconfig.test.js.map +1 -1
- package/dist/cli/wtlink.js +116 -73
- package/dist/cli/wtlink.js.map +1 -1
- package/dist/cli/wtstate.js +21 -2
- package/dist/cli/wtstate.js.map +1 -1
- package/dist/e2e/wt/interactive-menu.e2e.test.js +17 -17
- package/dist/e2e/wt/interactive-menu.e2e.test.js.map +1 -1
- package/dist/lib/ai/types.d.ts +12 -0
- package/dist/lib/ai/types.d.ts.map +1 -1
- package/dist/lib/ai/types.js.map +1 -1
- package/dist/lib/cleanpr/args.d.ts.map +1 -1
- package/dist/lib/cleanpr/args.js +20 -0
- package/dist/lib/cleanpr/args.js.map +1 -1
- package/dist/lib/cleanpr/types.d.ts +6 -0
- package/dist/lib/cleanpr/types.d.ts.map +1 -1
- package/dist/lib/cleanpr/worktree-info.d.ts.map +1 -1
- package/dist/lib/cleanpr/worktree-info.js +1 -6
- package/dist/lib/cleanpr/worktree-info.js.map +1 -1
- package/dist/lib/cleanpr/worktree-info.test.js +10 -13
- package/dist/lib/cleanpr/worktree-info.test.js.map +1 -1
- package/dist/lib/colors.d.ts +5 -0
- package/dist/lib/colors.d.ts.map +1 -1
- package/dist/lib/colors.js +13 -6
- package/dist/lib/colors.js.map +1 -1
- package/dist/lib/config-editor.d.ts.map +1 -1
- package/dist/lib/config-editor.js.map +1 -1
- package/dist/lib/config-migration/detector.d.ts +25 -0
- package/dist/lib/config-migration/detector.d.ts.map +1 -0
- package/dist/lib/config-migration/detector.js +372 -0
- package/dist/lib/config-migration/detector.js.map +1 -0
- package/dist/lib/config-migration/detector.test.d.ts +5 -0
- package/dist/lib/config-migration/detector.test.d.ts.map +1 -0
- package/dist/lib/config-migration/detector.test.js +201 -0
- package/dist/lib/config-migration/detector.test.js.map +1 -0
- package/dist/lib/config-migration/index.d.ts +29 -0
- package/dist/lib/config-migration/index.d.ts.map +1 -0
- package/dist/lib/config-migration/index.js +33 -0
- package/dist/lib/config-migration/index.js.map +1 -0
- package/dist/lib/config-migration/reporter.d.ts +53 -0
- package/dist/lib/config-migration/reporter.d.ts.map +1 -0
- package/dist/lib/config-migration/reporter.js +257 -0
- package/dist/lib/config-migration/reporter.js.map +1 -0
- package/dist/lib/config-migration/reporter.test.d.ts +5 -0
- package/dist/lib/config-migration/reporter.test.d.ts.map +1 -0
- package/dist/lib/config-migration/reporter.test.js +305 -0
- package/dist/lib/config-migration/reporter.test.js.map +1 -0
- package/dist/lib/config-migration/runner.d.ts +46 -0
- package/dist/lib/config-migration/runner.d.ts.map +1 -0
- package/dist/lib/config-migration/runner.js +364 -0
- package/dist/lib/config-migration/runner.js.map +1 -0
- package/dist/lib/config-migration/runner.test.d.ts +5 -0
- package/dist/lib/config-migration/runner.test.d.ts.map +1 -0
- package/dist/lib/config-migration/runner.test.js +235 -0
- package/dist/lib/config-migration/runner.test.js.map +1 -0
- package/dist/lib/config-migration/types.d.ts +120 -0
- package/dist/lib/config-migration/types.d.ts.map +1 -0
- package/dist/lib/config-migration/types.js +70 -0
- package/dist/lib/config-migration/types.js.map +1 -0
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config-validation.js +6 -0
- package/dist/lib/config-validation.js.map +1 -1
- package/dist/lib/config-validation.test.js +25 -0
- package/dist/lib/config-validation.test.js.map +1 -1
- package/dist/lib/config.d.ts +31 -7
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +2 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/config.test.js +3 -15
- package/dist/lib/config.test.js.map +1 -1
- package/dist/lib/constants.d.ts +12 -4
- package/dist/lib/constants.d.ts.map +1 -1
- package/dist/lib/constants.js +24 -5
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/constants.test.js +88 -29
- package/dist/lib/constants.test.js.map +1 -1
- package/dist/lib/deprecation.d.ts +18 -0
- package/dist/lib/deprecation.d.ts.map +1 -0
- package/dist/lib/deprecation.js +28 -0
- package/dist/lib/deprecation.js.map +1 -0
- package/dist/lib/deprecation.test.d.ts +2 -0
- package/dist/lib/deprecation.test.d.ts.map +1 -0
- package/dist/lib/deprecation.test.js +71 -0
- package/dist/lib/deprecation.test.js.map +1 -0
- package/dist/lib/hooks/confirmation.d.ts +49 -0
- package/dist/lib/hooks/confirmation.d.ts.map +1 -0
- package/dist/lib/hooks/confirmation.js +147 -0
- package/dist/lib/hooks/confirmation.js.map +1 -0
- package/dist/lib/hooks/confirmation.test.d.ts +7 -0
- package/dist/lib/hooks/confirmation.test.d.ts.map +1 -0
- package/dist/lib/hooks/confirmation.test.js +300 -0
- package/dist/lib/hooks/confirmation.test.js.map +1 -0
- package/dist/lib/hooks/executor.d.ts +16 -1
- package/dist/lib/hooks/executor.d.ts.map +1 -1
- package/dist/lib/hooks/executor.js +53 -4
- package/dist/lib/hooks/executor.js.map +1 -1
- package/dist/lib/hooks/index.d.ts +4 -2
- package/dist/lib/hooks/index.d.ts.map +1 -1
- package/dist/lib/hooks/index.js +3 -2
- package/dist/lib/hooks/index.js.map +1 -1
- package/dist/lib/hooks/types.d.ts +16 -0
- package/dist/lib/hooks/types.d.ts.map +1 -1
- package/dist/lib/hooks/types.js +12 -0
- package/dist/lib/hooks/types.js.map +1 -1
- package/dist/lib/logger.d.ts +40 -155
- package/dist/lib/logger.d.ts.map +1 -1
- package/dist/lib/logger.js +349 -420
- package/dist/lib/logger.js.map +1 -1
- package/dist/lib/logger.test.d.ts +10 -1
- package/dist/lib/logger.test.d.ts.map +1 -1
- package/dist/lib/logger.test.js +658 -258
- package/dist/lib/logger.test.js.map +1 -1
- package/dist/lib/lswt/action-executors.d.ts +2 -0
- package/dist/lib/lswt/action-executors.d.ts.map +1 -1
- package/dist/lib/lswt/action-executors.js +4 -3
- package/dist/lib/lswt/action-executors.js.map +1 -1
- package/dist/lib/lswt/action-executors.test.js +7 -0
- package/dist/lib/lswt/action-executors.test.js.map +1 -1
- package/dist/lib/lswt/args.d.ts.map +1 -1
- package/dist/lib/lswt/args.js +15 -1
- package/dist/lib/lswt/args.js.map +1 -1
- package/dist/lib/lswt/environment.d.ts +21 -2
- package/dist/lib/lswt/environment.d.ts.map +1 -1
- package/dist/lib/lswt/environment.js +73 -32
- package/dist/lib/lswt/environment.js.map +1 -1
- package/dist/lib/lswt/environment.test.js +79 -1
- package/dist/lib/lswt/environment.test.js.map +1 -1
- package/dist/lib/lswt/index.d.ts +1 -0
- package/dist/lib/lswt/index.d.ts.map +1 -1
- package/dist/lib/lswt/index.js +2 -0
- package/dist/lib/lswt/index.js.map +1 -1
- package/dist/lib/lswt/table.d.ts +15 -0
- package/dist/lib/lswt/table.d.ts.map +1 -0
- package/dist/lib/lswt/table.js +61 -0
- package/dist/lib/lswt/table.js.map +1 -0
- package/dist/lib/lswt/table.test.d.ts +5 -0
- package/dist/lib/lswt/table.test.d.ts.map +1 -0
- package/dist/lib/lswt/table.test.js +262 -0
- package/dist/lib/lswt/table.test.js.map +1 -0
- package/dist/lib/lswt/types.d.ts +4 -0
- package/dist/lib/lswt/types.d.ts.map +1 -1
- package/dist/lib/lswt/worktree-info.d.ts.map +1 -1
- package/dist/lib/lswt/worktree-info.js +1 -6
- package/dist/lib/lswt/worktree-info.js.map +1 -1
- package/dist/lib/lswt/worktree-info.test.js +5 -17
- package/dist/lib/lswt/worktree-info.test.js.map +1 -1
- package/dist/lib/newpr/args.d.ts.map +1 -1
- package/dist/lib/newpr/args.js +36 -1
- package/dist/lib/newpr/args.js.map +1 -1
- package/dist/lib/newpr/hook-runner.d.ts +11 -0
- package/dist/lib/newpr/hook-runner.d.ts.map +1 -1
- package/dist/lib/newpr/hook-runner.js +49 -1
- package/dist/lib/newpr/hook-runner.js.map +1 -1
- package/dist/lib/newpr/hook-runner.test.js +121 -0
- package/dist/lib/newpr/hook-runner.test.js.map +1 -1
- package/dist/lib/newpr/plan-generator.d.ts +121 -0
- package/dist/lib/newpr/plan-generator.d.ts.map +1 -0
- package/dist/lib/newpr/plan-generator.js +185 -0
- package/dist/lib/newpr/plan-generator.js.map +1 -0
- package/dist/lib/newpr/plan-generator.test.d.ts +7 -0
- package/dist/lib/newpr/plan-generator.test.d.ts.map +1 -0
- package/dist/lib/newpr/plan-generator.test.js +387 -0
- package/dist/lib/newpr/plan-generator.test.js.map +1 -0
- package/dist/lib/newpr/types.d.ts +12 -0
- package/dist/lib/newpr/types.d.ts.map +1 -1
- package/dist/lib/prs/actions.d.ts +5 -1
- package/dist/lib/prs/actions.d.ts.map +1 -1
- package/dist/lib/prs/actions.js +12 -10
- package/dist/lib/prs/actions.js.map +1 -1
- package/dist/lib/prs/actions.test.js +48 -5
- package/dist/lib/prs/actions.test.js.map +1 -1
- package/dist/lib/prs/command.d.ts +21 -0
- package/dist/lib/prs/command.d.ts.map +1 -0
- package/dist/lib/prs/command.js +175 -0
- package/dist/lib/prs/command.js.map +1 -0
- package/dist/lib/prs/command.test.d.ts +11 -0
- package/dist/lib/prs/command.test.d.ts.map +1 -0
- package/dist/lib/prs/command.test.js +409 -0
- package/dist/lib/prs/command.test.js.map +1 -0
- package/dist/lib/prs/interactive.d.ts.map +1 -1
- package/dist/lib/prs/interactive.js +15 -2
- package/dist/lib/prs/interactive.js.map +1 -1
- package/dist/lib/prs/interactive.test.js +153 -0
- package/dist/lib/prs/interactive.test.js.map +1 -1
- package/dist/lib/prs/types.d.ts +15 -0
- package/dist/lib/prs/types.d.ts.map +1 -1
- package/dist/lib/ui/error.d.ts +31 -0
- package/dist/lib/ui/error.d.ts.map +1 -0
- package/dist/lib/ui/error.js +47 -0
- package/dist/lib/ui/error.js.map +1 -0
- package/dist/lib/ui/error.test.d.ts +2 -0
- package/dist/lib/ui/error.test.d.ts.map +1 -0
- package/dist/lib/ui/error.test.js +143 -0
- package/dist/lib/ui/error.test.js.map +1 -0
- package/dist/lib/ui/index.d.ts +15 -0
- package/dist/lib/ui/index.d.ts.map +1 -0
- package/dist/lib/ui/index.js +19 -0
- package/dist/lib/ui/index.js.map +1 -0
- package/dist/lib/ui/output.d.ts +18 -0
- package/dist/lib/ui/output.d.ts.map +1 -0
- package/dist/lib/ui/output.js +31 -0
- package/dist/lib/ui/output.js.map +1 -0
- package/dist/lib/ui/output.test.d.ts +2 -0
- package/dist/lib/ui/output.test.d.ts.map +1 -0
- package/dist/lib/ui/output.test.js +59 -0
- package/dist/lib/ui/output.test.js.map +1 -0
- package/dist/lib/ui/spinner.d.ts +10 -0
- package/dist/lib/ui/spinner.d.ts.map +1 -0
- package/dist/lib/ui/spinner.js +10 -0
- package/dist/lib/ui/spinner.js.map +1 -0
- package/dist/lib/ui/status.d.ts +65 -0
- package/dist/lib/ui/status.d.ts.map +1 -0
- package/dist/lib/ui/status.js +100 -0
- package/dist/lib/ui/status.js.map +1 -0
- package/dist/lib/ui/status.test.d.ts +2 -0
- package/dist/lib/ui/status.test.d.ts.map +1 -0
- package/dist/lib/ui/status.test.js +158 -0
- package/dist/lib/ui/status.test.js.map +1 -0
- package/dist/lib/ui/table.d.ts +39 -0
- package/dist/lib/ui/table.d.ts.map +1 -0
- package/dist/lib/ui/table.js +45 -0
- package/dist/lib/ui/table.js.map +1 -0
- package/dist/lib/ui/table.test.d.ts +2 -0
- package/dist/lib/ui/table.test.d.ts.map +1 -0
- package/dist/lib/ui/table.test.js +115 -0
- package/dist/lib/ui/table.test.js.map +1 -0
- package/dist/lib/ui/theme.d.ts +34 -0
- package/dist/lib/ui/theme.d.ts.map +1 -0
- package/dist/lib/ui/theme.js +37 -0
- package/dist/lib/ui/theme.js.map +1 -0
- package/dist/lib/ui/theme.test.d.ts +2 -0
- package/dist/lib/ui/theme.test.d.ts.map +1 -0
- package/dist/lib/ui/theme.test.js +76 -0
- package/dist/lib/ui/theme.test.js.map +1 -0
- package/dist/lib/wtconfig/environment.d.ts +18 -1
- package/dist/lib/wtconfig/environment.d.ts.map +1 -1
- package/dist/lib/wtconfig/environment.js +60 -24
- package/dist/lib/wtconfig/environment.js.map +1 -1
- package/dist/lib/wtconfig/environment.test.js +45 -1
- package/dist/lib/wtconfig/environment.test.js.map +1 -1
- package/dist/lib/wtlink/config-manifest.test.js +26 -0
- package/dist/lib/wtlink/config-manifest.test.js.map +1 -1
- package/dist/lib/wtlink/link-configs.js +7 -7
- package/dist/lib/wtlink/link-configs.js.map +1 -1
- package/dist/lib/wtlink/validate-manifest.d.ts.map +1 -1
- package/dist/lib/wtlink/validate-manifest.js +5 -5
- package/dist/lib/wtlink/validate-manifest.js.map +1 -1
- package/dist/lib/wtstate/args.d.ts.map +1 -1
- package/dist/lib/wtstate/args.js +2 -0
- package/dist/lib/wtstate/args.js.map +1 -1
- package/dist/mcp/server.d.ts +2 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +264 -44
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/server.test.js +111 -0
- package/dist/mcp/server.test.js.map +1 -1
- package/package.json +3 -1
- package/schemas/worktreerc.schema.json +23 -0
package/dist/lib/logger.js
CHANGED
|
@@ -1,475 +1,404 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Logging system for git-worktree-tools
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* -
|
|
6
|
-
* -
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* - Color support for console output
|
|
4
|
+
* Consola-based singleton logger with:
|
|
5
|
+
* - AuditFileReporter: persistent audit log with size-based rotation
|
|
6
|
+
* - ConditionalStderrReporter: verbose/quiet aware stderr output
|
|
7
|
+
* - Audit session tracking (command, cwd, branch, exit code, duration)
|
|
8
|
+
* - DEBUG=newpr deprecation handling
|
|
10
9
|
*
|
|
11
10
|
* Configuration sources (in order of priority):
|
|
12
|
-
* 1. CLI flags (--verbose, --
|
|
13
|
-
* 2. Environment variables (GWT_LOG_LEVEL,
|
|
14
|
-
* 3.
|
|
11
|
+
* 1. CLI flags (--verbose, --quiet, --no-color)
|
|
12
|
+
* 2. Environment variables (GWT_LOG_LEVEL, DEBUG)
|
|
13
|
+
* 3. Default (INFO)
|
|
15
14
|
*/
|
|
16
15
|
import fs from 'fs';
|
|
17
16
|
import path from 'path';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
17
|
+
import { execSync } from 'child_process';
|
|
18
|
+
import { createConsola } from 'consola';
|
|
19
|
+
import { LogLevel, MAX_LOG_FILE_SIZE, MAX_LOG_FILES, getGlobalDataDir } from './constants.js';
|
|
20
|
+
import { setColorEnabled } from './colors.js';
|
|
20
21
|
export { LogLevel };
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Module-level state
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
/** Whether the DEBUG=newpr deprecation warning has been printed */
|
|
26
|
+
let deprecationWarned = false;
|
|
27
|
+
/** Whether JSON output mode is active */
|
|
28
|
+
let jsonMode = false;
|
|
29
|
+
/** Audit session context for the process exit handler */
|
|
30
|
+
let auditContext = {};
|
|
31
|
+
/** Track whether exit handler has been registered */
|
|
32
|
+
let exitHandlerRegistered = false;
|
|
33
|
+
/** Track whether audit file warning has been issued */
|
|
34
|
+
let auditFileWarned = false;
|
|
35
|
+
/** Reference to the AuditFileReporter for exit-time sync write */
|
|
36
|
+
let activeAuditReporter = null;
|
|
37
|
+
// ---------------------------------------------------------------------------
|
|
38
|
+
// AuditFileReporter
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
21
40
|
/**
|
|
22
|
-
*
|
|
41
|
+
* Writes log entries to a persistent audit file with size-based rotation.
|
|
42
|
+
* Human-readable text lines by default; JSONL when json mode is active.
|
|
23
43
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
warn: LogLevel.WARN,
|
|
30
|
-
warning: LogLevel.WARN,
|
|
31
|
-
info: LogLevel.INFO,
|
|
32
|
-
debug: LogLevel.DEBUG,
|
|
33
|
-
trace: LogLevel.TRACE,
|
|
34
|
-
verbose: LogLevel.DEBUG,
|
|
35
|
-
'0': LogLevel.SILENT,
|
|
36
|
-
'1': LogLevel.ERROR,
|
|
37
|
-
'2': LogLevel.WARN,
|
|
38
|
-
'3': LogLevel.INFO,
|
|
39
|
-
'4': LogLevel.DEBUG,
|
|
40
|
-
'5': LogLevel.TRACE,
|
|
41
|
-
};
|
|
42
|
-
return mapping[normalized];
|
|
43
|
-
}
|
|
44
|
-
/**
|
|
45
|
-
* Get log level name
|
|
46
|
-
*/
|
|
47
|
-
function getLevelName(level) {
|
|
48
|
-
const names = {
|
|
49
|
-
[LogLevel.SILENT]: 'SILENT',
|
|
50
|
-
[LogLevel.ERROR]: 'ERROR',
|
|
51
|
-
[LogLevel.WARN]: 'WARN',
|
|
52
|
-
[LogLevel.INFO]: 'INFO',
|
|
53
|
-
[LogLevel.DEBUG]: 'DEBUG',
|
|
54
|
-
[LogLevel.TRACE]: 'TRACE',
|
|
55
|
-
};
|
|
56
|
-
return names[level] || 'UNKNOWN';
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Format a log message for console output with colors
|
|
60
|
-
*/
|
|
61
|
-
function formatConsoleMessage(level, message, timestamp, useColors) {
|
|
62
|
-
const parts = [];
|
|
63
|
-
if (timestamp) {
|
|
64
|
-
parts.push(useColors ? gray(`[${timestamp}]`) : `[${timestamp}]`);
|
|
65
|
-
}
|
|
66
|
-
const levelStr = getLevelName(level).padEnd(5);
|
|
67
|
-
let formattedLevel;
|
|
68
|
-
let formattedMessage;
|
|
69
|
-
if (useColors) {
|
|
70
|
-
switch (level) {
|
|
71
|
-
case LogLevel.ERROR:
|
|
72
|
-
formattedLevel = red(bold(levelStr));
|
|
73
|
-
formattedMessage = red(message);
|
|
74
|
-
break;
|
|
75
|
-
case LogLevel.WARN:
|
|
76
|
-
formattedLevel = yellow(bold(levelStr));
|
|
77
|
-
formattedMessage = yellow(message);
|
|
78
|
-
break;
|
|
79
|
-
case LogLevel.DEBUG:
|
|
80
|
-
case LogLevel.TRACE:
|
|
81
|
-
formattedLevel = gray(levelStr);
|
|
82
|
-
formattedMessage = gray(message);
|
|
83
|
-
break;
|
|
84
|
-
default:
|
|
85
|
-
formattedLevel = cyan(levelStr);
|
|
86
|
-
formattedMessage = message;
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
formattedLevel = levelStr;
|
|
91
|
-
formattedMessage = message;
|
|
92
|
-
}
|
|
93
|
-
parts.push(formattedLevel);
|
|
94
|
-
parts.push(formattedMessage);
|
|
95
|
-
return parts.join(' ');
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Singleton Logger class
|
|
99
|
-
*/
|
|
100
|
-
class Logger {
|
|
101
|
-
static instance = null;
|
|
102
|
-
level = DEFAULT_LOG_LEVEL;
|
|
103
|
-
logFile = null;
|
|
104
|
-
fileStream = null;
|
|
105
|
-
timestamps = true;
|
|
106
|
-
colors = true;
|
|
107
|
-
consoleOutput = true;
|
|
108
|
-
context = null;
|
|
109
|
-
initialized = false;
|
|
110
|
-
constructor() {
|
|
111
|
-
// Private constructor for singleton
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Get the singleton logger instance
|
|
115
|
-
*/
|
|
116
|
-
static getInstance() {
|
|
117
|
-
if (!Logger.instance) {
|
|
118
|
-
Logger.instance = new Logger();
|
|
119
|
-
}
|
|
120
|
-
return Logger.instance;
|
|
121
|
-
}
|
|
122
|
-
/**
|
|
123
|
-
* Initialize the logger with configuration
|
|
124
|
-
* Should be called early in CLI startup
|
|
125
|
-
*/
|
|
126
|
-
initialize(config = {}) {
|
|
127
|
-
// Determine log level from multiple sources (priority order)
|
|
128
|
-
// 1. Explicit config (from CLI flags)
|
|
129
|
-
// 2. Environment variable
|
|
130
|
-
// 3. Default
|
|
131
|
-
if (config.level !== undefined) {
|
|
132
|
-
this.level = config.level;
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
const envLevel = process.env.GWT_LOG_LEVEL;
|
|
136
|
-
if (envLevel) {
|
|
137
|
-
const parsed = parseLogLevel(envLevel);
|
|
138
|
-
if (parsed !== undefined) {
|
|
139
|
-
this.level = parsed;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
// Determine log file from multiple sources
|
|
144
|
-
if (config.logFile !== undefined) {
|
|
145
|
-
this.logFile = config.logFile;
|
|
146
|
-
}
|
|
147
|
-
else {
|
|
148
|
-
const envLogFile = process.env.GWT_LOG_FILE;
|
|
149
|
-
if (envLogFile) {
|
|
150
|
-
this.logFile = envLogFile;
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
if (config.timestamps !== undefined) {
|
|
154
|
-
this.timestamps = config.timestamps;
|
|
155
|
-
}
|
|
156
|
-
if (config.colors !== undefined) {
|
|
157
|
-
this.colors = config.colors;
|
|
158
|
-
}
|
|
159
|
-
if (config.consoleOutput !== undefined) {
|
|
160
|
-
this.consoleOutput = config.consoleOutput;
|
|
161
|
-
}
|
|
162
|
-
// Set up file logging if configured
|
|
163
|
-
if (this.logFile) {
|
|
164
|
-
this.setupFileLogging(this.logFile);
|
|
165
|
-
}
|
|
166
|
-
this.initialized = true;
|
|
167
|
-
}
|
|
168
|
-
/**
|
|
169
|
-
* Set up file logging with rotation
|
|
170
|
-
*/
|
|
171
|
-
setupFileLogging(filePath) {
|
|
44
|
+
class AuditFileReporter {
|
|
45
|
+
stream = null;
|
|
46
|
+
filePath;
|
|
47
|
+
constructor(filePath) {
|
|
48
|
+
this.filePath = filePath;
|
|
172
49
|
try {
|
|
173
|
-
// Ensure directory exists
|
|
174
50
|
const dir = path.dirname(filePath);
|
|
175
51
|
fs.mkdirSync(dir, { recursive: true });
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
if (
|
|
180
|
-
|
|
52
|
+
rotateIfNeeded(filePath);
|
|
53
|
+
this.stream = fs.createWriteStream(filePath, { flags: 'a' });
|
|
54
|
+
this.stream.on('error', (err) => {
|
|
55
|
+
if (!auditFileWarned) {
|
|
56
|
+
auditFileWarned = true;
|
|
57
|
+
process.stderr.write(`[gwt] Audit log write error: ${err.message}\n`);
|
|
181
58
|
}
|
|
182
|
-
|
|
183
|
-
// Open file stream in append mode
|
|
184
|
-
this.fileStream = fs.createWriteStream(filePath, { flags: 'a' });
|
|
185
|
-
this.fileStream.on('error', (err) => {
|
|
186
|
-
console.error(`Logger: Failed to write to log file: ${err.message}`);
|
|
187
|
-
this.fileStream = null;
|
|
59
|
+
this.stream = null;
|
|
188
60
|
});
|
|
189
61
|
}
|
|
190
62
|
catch (err) {
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
/**
|
|
196
|
-
* Rotate log files
|
|
197
|
-
*/
|
|
198
|
-
rotateLogFiles(filePath) {
|
|
199
|
-
try {
|
|
200
|
-
// Shift existing rotated files
|
|
201
|
-
for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
202
|
-
const older = `${filePath}.${i}`;
|
|
203
|
-
const newer = i === 1 ? filePath : `${filePath}.${i - 1}`;
|
|
204
|
-
if (fs.existsSync(newer)) {
|
|
205
|
-
if (i === MAX_LOG_FILES - 1 && fs.existsSync(older)) {
|
|
206
|
-
fs.unlinkSync(older);
|
|
207
|
-
}
|
|
208
|
-
fs.renameSync(newer, older);
|
|
209
|
-
}
|
|
63
|
+
if (!auditFileWarned) {
|
|
64
|
+
auditFileWarned = true;
|
|
65
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
66
|
+
process.stderr.write(`[gwt] Failed to open audit log: ${msg}\n`);
|
|
210
67
|
}
|
|
211
68
|
}
|
|
212
|
-
catch (err) {
|
|
213
|
-
const message = err instanceof Error ? err.message : String(err);
|
|
214
|
-
console.error(`Logger: Failed to rotate log files: ${message}`);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Set the current context (e.g., command name)
|
|
219
|
-
*/
|
|
220
|
-
setContext(context) {
|
|
221
|
-
this.context = context;
|
|
222
69
|
}
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
*/
|
|
226
|
-
setLevel(level) {
|
|
227
|
-
this.level = level;
|
|
228
|
-
}
|
|
229
|
-
/**
|
|
230
|
-
* Get the current log level
|
|
231
|
-
*/
|
|
232
|
-
getLevel() {
|
|
233
|
-
return this.level;
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Check if a log level is enabled
|
|
237
|
-
*/
|
|
238
|
-
isLevelEnabled(level) {
|
|
239
|
-
return level <= this.level;
|
|
240
|
-
}
|
|
241
|
-
/**
|
|
242
|
-
* Check if the logger is in debug mode
|
|
243
|
-
*/
|
|
244
|
-
isDebug() {
|
|
245
|
-
return this.level >= LogLevel.DEBUG;
|
|
246
|
-
}
|
|
247
|
-
/**
|
|
248
|
-
* Check if the logger is in trace mode
|
|
249
|
-
*/
|
|
250
|
-
isTrace() {
|
|
251
|
-
return this.level >= LogLevel.TRACE;
|
|
252
|
-
}
|
|
253
|
-
/**
|
|
254
|
-
* Internal log method
|
|
255
|
-
*/
|
|
256
|
-
log(level, message, ...args) {
|
|
257
|
-
this.logWithContext(level, this.context, message, ...args);
|
|
258
|
-
}
|
|
259
|
-
/**
|
|
260
|
-
* Internal log method with explicit context (thread-safe for child loggers)
|
|
261
|
-
*/
|
|
262
|
-
logWithContext(level, context, message, ...args) {
|
|
263
|
-
if (level > this.level) {
|
|
70
|
+
log(logObj) {
|
|
71
|
+
if (!this.stream)
|
|
264
72
|
return;
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
73
|
+
try {
|
|
74
|
+
const timestamp = new Date().toISOString();
|
|
75
|
+
const levelName = levelToName(logObj.level);
|
|
76
|
+
const tag = logObj.tag ? ` [${logObj.tag}]` : '';
|
|
77
|
+
const message = formatLogArgs(logObj.args);
|
|
78
|
+
if (jsonMode) {
|
|
79
|
+
const entry = {
|
|
80
|
+
timestamp,
|
|
81
|
+
level: levelName,
|
|
82
|
+
...(logObj.tag ? { tag: logObj.tag } : {}),
|
|
83
|
+
message,
|
|
84
|
+
};
|
|
85
|
+
this.stream.write(JSON.stringify(entry) + '\n');
|
|
276
86
|
}
|
|
277
87
|
else {
|
|
278
|
-
|
|
88
|
+
this.stream.write(`[${timestamp}] ${levelName}${tag} ${message}\n`);
|
|
279
89
|
}
|
|
280
90
|
}
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const entry = {
|
|
284
|
-
timestamp: timestamp || new Date().toISOString(),
|
|
285
|
-
level: getLevelName(level),
|
|
286
|
-
message: formattedMessage,
|
|
287
|
-
};
|
|
288
|
-
if (context) {
|
|
289
|
-
entry.context = context;
|
|
290
|
-
}
|
|
291
|
-
if (args.length > 0 && typeof args[0] === 'object') {
|
|
292
|
-
entry.data = args[0];
|
|
293
|
-
}
|
|
294
|
-
this.fileStream.write(JSON.stringify(entry) + '\n');
|
|
91
|
+
catch {
|
|
92
|
+
// Swallow write errors silently after first warning
|
|
295
93
|
}
|
|
296
94
|
}
|
|
297
|
-
/**
|
|
298
|
-
* Format message with arguments (simple substitution)
|
|
299
|
-
*/
|
|
300
|
-
formatMessage(message, args) {
|
|
301
|
-
let result = message;
|
|
302
|
-
for (const arg of args) {
|
|
303
|
-
const replacement = typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
|
|
304
|
-
result = result.replace('%s', replacement);
|
|
305
|
-
result = result.replace('%d', replacement);
|
|
306
|
-
result = result.replace('%j', typeof arg === 'object' ? JSON.stringify(arg) : replacement);
|
|
307
|
-
result = result.replace('%o', typeof arg === 'object' ? JSON.stringify(arg) : replacement);
|
|
308
|
-
}
|
|
309
|
-
// Append remaining args if placeholders exhausted
|
|
310
|
-
const placeholders = (message.match(/%[sdjo]/g) || []).length;
|
|
311
|
-
if (args.length > placeholders) {
|
|
312
|
-
const extra = args
|
|
313
|
-
.slice(placeholders)
|
|
314
|
-
.map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a)));
|
|
315
|
-
result += ' ' + extra.join(' ');
|
|
316
|
-
}
|
|
317
|
-
return result;
|
|
318
|
-
}
|
|
319
|
-
/**
|
|
320
|
-
* Log an error message
|
|
321
|
-
*/
|
|
322
|
-
error(message, ...args) {
|
|
323
|
-
this.log(LogLevel.ERROR, message, ...args);
|
|
324
|
-
}
|
|
325
|
-
/**
|
|
326
|
-
* Log a warning message
|
|
327
|
-
*/
|
|
328
|
-
warn(message, ...args) {
|
|
329
|
-
this.log(LogLevel.WARN, message, ...args);
|
|
330
|
-
}
|
|
331
|
-
/**
|
|
332
|
-
* Log an info message
|
|
333
|
-
*/
|
|
334
|
-
info(message, ...args) {
|
|
335
|
-
this.log(LogLevel.INFO, message, ...args);
|
|
336
|
-
}
|
|
337
|
-
/**
|
|
338
|
-
* Log a debug message
|
|
339
|
-
*/
|
|
340
|
-
debug(message, ...args) {
|
|
341
|
-
this.log(LogLevel.DEBUG, message, ...args);
|
|
342
|
-
}
|
|
343
|
-
/**
|
|
344
|
-
* Log a trace message
|
|
345
|
-
*/
|
|
346
|
-
trace(message, ...args) {
|
|
347
|
-
this.log(LogLevel.TRACE, message, ...args);
|
|
348
|
-
}
|
|
349
|
-
/**
|
|
350
|
-
* Log an error with stack trace
|
|
351
|
-
*/
|
|
352
|
-
errorWithStack(err, message) {
|
|
353
|
-
const msg = message ? `${message}: ${err.message}` : err.message;
|
|
354
|
-
this.error(msg);
|
|
355
|
-
if (this.level >= LogLevel.DEBUG && err.stack) {
|
|
356
|
-
this.debug(err.stack);
|
|
357
|
-
}
|
|
358
|
-
}
|
|
359
|
-
/**
|
|
360
|
-
* Create a child logger with a specific context
|
|
361
|
-
*/
|
|
362
|
-
child(context) {
|
|
363
|
-
return new ChildLogger(this, context);
|
|
364
|
-
}
|
|
365
|
-
/**
|
|
366
|
-
* Close the logger (flush file stream)
|
|
367
|
-
*/
|
|
368
95
|
close() {
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
96
|
+
if (this.stream) {
|
|
97
|
+
const stream = this.stream;
|
|
98
|
+
this.stream = null;
|
|
99
|
+
// Close the fd synchronously so callers can immediately delete the file.
|
|
100
|
+
// On Windows, stream.end()/destroy() close the fd asynchronously, leaving
|
|
101
|
+
// the file locked until the next tick.
|
|
102
|
+
const streamInternal = stream;
|
|
103
|
+
const fd = streamInternal.fd;
|
|
104
|
+
if (typeof fd === 'number') {
|
|
105
|
+
// Null out the fd BEFORE closing so destroy() won't try to close it
|
|
106
|
+
// again (which would risk closing a reused fd number).
|
|
107
|
+
streamInternal.fd = null;
|
|
108
|
+
try {
|
|
109
|
+
fs.closeSync(fd);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
/* best effort */
|
|
113
|
+
}
|
|
378
114
|
}
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
* Get the default log file path
|
|
383
|
-
*/
|
|
384
|
-
static getDefaultLogFilePath() {
|
|
385
|
-
return path.join(getGlobalLogDir(), 'wt.log');
|
|
386
|
-
}
|
|
387
|
-
/**
|
|
388
|
-
* Reset the singleton instance (for testing)
|
|
389
|
-
*/
|
|
390
|
-
static reset() {
|
|
391
|
-
if (Logger.instance) {
|
|
392
|
-
Logger.instance.close();
|
|
393
|
-
Logger.instance = null;
|
|
115
|
+
stream.removeAllListeners('error');
|
|
116
|
+
stream.on('error', () => { });
|
|
117
|
+
stream.destroy();
|
|
394
118
|
}
|
|
395
119
|
}
|
|
396
120
|
}
|
|
121
|
+
// ---------------------------------------------------------------------------
|
|
122
|
+
// ConditionalStderrReporter
|
|
123
|
+
// ---------------------------------------------------------------------------
|
|
397
124
|
/**
|
|
398
|
-
*
|
|
399
|
-
*
|
|
125
|
+
* Writes to stderr based on log level and verbose mode.
|
|
126
|
+
* WARN and ERROR always print; DEBUG/INFO/TRACE only print when verbose=true.
|
|
400
127
|
*/
|
|
401
|
-
class
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
constructor(
|
|
405
|
-
this.
|
|
406
|
-
this.
|
|
128
|
+
class ConditionalStderrReporter {
|
|
129
|
+
verbose;
|
|
130
|
+
useColors;
|
|
131
|
+
constructor(verbose, useColors) {
|
|
132
|
+
this.verbose = verbose;
|
|
133
|
+
this.useColors = useColors;
|
|
134
|
+
}
|
|
135
|
+
log(logObj) {
|
|
136
|
+
// Level < 2 means warn (1) or error/fatal (0) — always print
|
|
137
|
+
// Level >= 2 means info (3), debug (4), trace (5) — only if verbose
|
|
138
|
+
if (logObj.level >= 2 && !this.verbose) {
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const levelName = levelToName(logObj.level);
|
|
142
|
+
const tag = logObj.tag ? ` [${logObj.tag}]` : '';
|
|
143
|
+
const message = formatLogArgs(logObj.args);
|
|
144
|
+
let prefix;
|
|
145
|
+
if (this.useColors) {
|
|
146
|
+
prefix = colorizeLevel(levelName, logObj.level);
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
prefix = `[${levelName}]`;
|
|
150
|
+
}
|
|
151
|
+
process.stderr.write(`${prefix}${tag} ${message}\n`);
|
|
407
152
|
}
|
|
408
|
-
|
|
409
|
-
|
|
153
|
+
}
|
|
154
|
+
// ---------------------------------------------------------------------------
|
|
155
|
+
// Rotation logic
|
|
156
|
+
// ---------------------------------------------------------------------------
|
|
157
|
+
function rotateIfNeeded(filePath) {
|
|
158
|
+
try {
|
|
159
|
+
if (!fs.existsSync(filePath))
|
|
160
|
+
return;
|
|
161
|
+
const stats = fs.statSync(filePath);
|
|
162
|
+
if (stats.size <= MAX_LOG_FILE_SIZE)
|
|
163
|
+
return;
|
|
164
|
+
// Shift: audit.log.2 deleted, audit.log.1 -> .2, audit.log -> .1
|
|
165
|
+
for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
|
|
166
|
+
const older = `${filePath}.${i}`;
|
|
167
|
+
const newer = i === 1 ? filePath : `${filePath}.${i - 1}`;
|
|
168
|
+
if (fs.existsSync(newer)) {
|
|
169
|
+
if (i === MAX_LOG_FILES - 1 && fs.existsSync(older)) {
|
|
170
|
+
fs.unlinkSync(older);
|
|
171
|
+
}
|
|
172
|
+
fs.renameSync(newer, older);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
410
175
|
}
|
|
411
|
-
|
|
412
|
-
|
|
176
|
+
catch {
|
|
177
|
+
// Best-effort rotation — do not crash
|
|
413
178
|
}
|
|
414
|
-
|
|
415
|
-
|
|
179
|
+
}
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
// Helpers
|
|
182
|
+
// ---------------------------------------------------------------------------
|
|
183
|
+
function levelToName(level) {
|
|
184
|
+
if (level <= 0) {
|
|
185
|
+
// 0 = error/fatal, negative = silent (shouldn't log)
|
|
186
|
+
return level === 0 ? 'ERROR' : 'SILENT';
|
|
187
|
+
}
|
|
188
|
+
switch (level) {
|
|
189
|
+
case 1:
|
|
190
|
+
return 'WARN';
|
|
191
|
+
case 2:
|
|
192
|
+
return 'LOG';
|
|
193
|
+
case 3:
|
|
194
|
+
return 'INFO';
|
|
195
|
+
case 4:
|
|
196
|
+
return 'DEBUG';
|
|
197
|
+
case 5:
|
|
198
|
+
return 'TRACE';
|
|
199
|
+
default:
|
|
200
|
+
return level > 5 ? 'TRACE' : 'ERROR';
|
|
416
201
|
}
|
|
417
|
-
|
|
418
|
-
|
|
202
|
+
}
|
|
203
|
+
function colorizeLevel(name, level) {
|
|
204
|
+
// ANSI color codes
|
|
205
|
+
const RED = '\x1b[31m';
|
|
206
|
+
const YELLOW = '\x1b[33m';
|
|
207
|
+
const CYAN = '\x1b[36m';
|
|
208
|
+
const GRAY = '\x1b[90m';
|
|
209
|
+
const RESET = '\x1b[0m';
|
|
210
|
+
switch (true) {
|
|
211
|
+
case level <= 0:
|
|
212
|
+
return `${RED}[${name}]${RESET}`;
|
|
213
|
+
case level === 1:
|
|
214
|
+
return `${YELLOW}[${name}]${RESET}`;
|
|
215
|
+
case level <= 3:
|
|
216
|
+
return `${CYAN}[${name}]${RESET}`;
|
|
217
|
+
default:
|
|
218
|
+
return `${GRAY}[${name}]${RESET}`;
|
|
419
219
|
}
|
|
420
|
-
|
|
421
|
-
|
|
220
|
+
}
|
|
221
|
+
function formatLogArgs(args) {
|
|
222
|
+
return args
|
|
223
|
+
.map((a) => (typeof a === 'object' && a !== null ? JSON.stringify(a) : String(a)))
|
|
224
|
+
.join(' ');
|
|
225
|
+
}
|
|
226
|
+
function getCurrentGitBranch() {
|
|
227
|
+
try {
|
|
228
|
+
return execSync('git rev-parse --abbrev-ref HEAD', {
|
|
229
|
+
encoding: 'utf8',
|
|
230
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
231
|
+
}).trim();
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return 'unknown';
|
|
422
235
|
}
|
|
423
236
|
}
|
|
237
|
+
// ---------------------------------------------------------------------------
|
|
238
|
+
// Logger singleton
|
|
239
|
+
// ---------------------------------------------------------------------------
|
|
424
240
|
/**
|
|
425
|
-
* The singleton logger instance
|
|
241
|
+
* The singleton consola logger instance.
|
|
242
|
+
* Starts with empty reporters — call initializeLogger() to configure.
|
|
426
243
|
*/
|
|
427
|
-
export const logger =
|
|
244
|
+
export const logger = createConsola({
|
|
245
|
+
level: 3, // INFO default
|
|
246
|
+
reporters: [],
|
|
247
|
+
});
|
|
428
248
|
/**
|
|
429
|
-
*
|
|
430
|
-
*
|
|
249
|
+
* Configure the logger with CLI flags, env vars, and reporters.
|
|
250
|
+
* Must be called early in CLI startup. Safe to call multiple times
|
|
251
|
+
* (replaces reporters each time).
|
|
431
252
|
*/
|
|
432
|
-
export function initializeLogger(options) {
|
|
253
|
+
export function initializeLogger(options = {}) {
|
|
254
|
+
// ----------------------------------------------------------
|
|
255
|
+
// 1. Resolve level: CLI flag > env var > default
|
|
256
|
+
// ----------------------------------------------------------
|
|
433
257
|
let level;
|
|
434
|
-
// Priority: CLI flags > env vars > config
|
|
435
258
|
if (options.quiet) {
|
|
436
|
-
level =
|
|
259
|
+
level = 0; // error only
|
|
437
260
|
}
|
|
438
|
-
else if (options.
|
|
439
|
-
level =
|
|
440
|
-
}
|
|
441
|
-
else if (options.verbose !== undefined) {
|
|
442
|
-
if (typeof options.verbose === 'number') {
|
|
443
|
-
// -v = DEBUG, -vv = TRACE
|
|
444
|
-
level = options.verbose >= 2 ? LogLevel.TRACE : LogLevel.DEBUG;
|
|
445
|
-
}
|
|
446
|
-
else if (options.verbose) {
|
|
447
|
-
level = LogLevel.DEBUG;
|
|
448
|
-
}
|
|
261
|
+
else if (options.verbose) {
|
|
262
|
+
level = 4; // debug
|
|
449
263
|
}
|
|
450
264
|
else if (process.env.GWT_LOG_LEVEL) {
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
logFile = options.logFile;
|
|
463
|
-
}
|
|
464
|
-
else if (process.env.GWT_LOG_FILE) {
|
|
465
|
-
logFile = process.env.GWT_LOG_FILE;
|
|
265
|
+
const parsed = parseLogLevel(process.env.GWT_LOG_LEVEL);
|
|
266
|
+
level = parsed !== undefined ? parsed : 3;
|
|
267
|
+
}
|
|
268
|
+
else if (process.env.DEBUG === 'newpr' ||
|
|
269
|
+
process.env.DEBUG === '*' ||
|
|
270
|
+
process.env.DEBUG === '1') {
|
|
271
|
+
level = 4; // debug
|
|
272
|
+
if (!deprecationWarned) {
|
|
273
|
+
deprecationWarned = true;
|
|
274
|
+
process.stderr.write('WARNING: DEBUG=newpr is deprecated, use GWT_LOG_LEVEL=debug\n');
|
|
275
|
+
}
|
|
466
276
|
}
|
|
467
|
-
else
|
|
468
|
-
|
|
277
|
+
else {
|
|
278
|
+
level = 3; // info (default)
|
|
279
|
+
}
|
|
280
|
+
logger.level = level;
|
|
281
|
+
// ----------------------------------------------------------
|
|
282
|
+
// 2. Handle color
|
|
283
|
+
// ----------------------------------------------------------
|
|
284
|
+
const useColors = !options.noColor && process.env.NO_COLOR === undefined;
|
|
285
|
+
if (options.noColor) {
|
|
286
|
+
setColorEnabled(false);
|
|
287
|
+
}
|
|
288
|
+
// ----------------------------------------------------------
|
|
289
|
+
// 3. JSON mode
|
|
290
|
+
// ----------------------------------------------------------
|
|
291
|
+
jsonMode = options.json ?? false;
|
|
292
|
+
// ----------------------------------------------------------
|
|
293
|
+
// 4. Build reporters
|
|
294
|
+
// ----------------------------------------------------------
|
|
295
|
+
const reporters = [];
|
|
296
|
+
// Audit file reporter (always on)
|
|
297
|
+
try {
|
|
298
|
+
const auditPath = path.join(getGlobalDataDir(), 'audit.log');
|
|
299
|
+
const auditReporter = new AuditFileReporter(auditPath);
|
|
300
|
+
reporters.push(auditReporter);
|
|
301
|
+
activeAuditReporter = auditReporter;
|
|
302
|
+
}
|
|
303
|
+
catch {
|
|
304
|
+
// Best effort — continue without audit file
|
|
305
|
+
}
|
|
306
|
+
// Stderr reporter
|
|
307
|
+
const verbose = options.verbose ?? false;
|
|
308
|
+
reporters.push(new ConditionalStderrReporter(verbose, useColors));
|
|
309
|
+
logger.setReporters(reporters);
|
|
310
|
+
// ----------------------------------------------------------
|
|
311
|
+
// 5. Audit session tracking
|
|
312
|
+
// ----------------------------------------------------------
|
|
313
|
+
if (options.commandName) {
|
|
314
|
+
auditContext.command = options.commandName;
|
|
315
|
+
auditContext.startTime = Date.now();
|
|
316
|
+
auditContext.cwd = process.cwd();
|
|
317
|
+
auditContext.gitBranch = getCurrentGitBranch();
|
|
318
|
+
}
|
|
319
|
+
if (!exitHandlerRegistered) {
|
|
320
|
+
exitHandlerRegistered = true;
|
|
321
|
+
process.on('exit', (code) => {
|
|
322
|
+
if (!auditContext.command || !activeAuditReporter)
|
|
323
|
+
return;
|
|
324
|
+
const duration = auditContext.startTime ? Date.now() - auditContext.startTime : 0;
|
|
325
|
+
const entry = {
|
|
326
|
+
type: 'session',
|
|
327
|
+
timestamp: new Date().toISOString(),
|
|
328
|
+
command: auditContext.command,
|
|
329
|
+
cwd: auditContext.cwd,
|
|
330
|
+
gitBranch: auditContext.gitBranch,
|
|
331
|
+
exitCode: code,
|
|
332
|
+
durationMs: duration,
|
|
333
|
+
};
|
|
334
|
+
if (auditContext.worktreePath)
|
|
335
|
+
entry.worktreePath = auditContext.worktreePath;
|
|
336
|
+
if (auditContext.prNumber !== undefined)
|
|
337
|
+
entry.prNumber = auditContext.prNumber;
|
|
338
|
+
// Must be synchronous — Node.js does not process async after 'exit'
|
|
339
|
+
try {
|
|
340
|
+
const logPath = activeAuditReporter.filePath;
|
|
341
|
+
const line = jsonMode
|
|
342
|
+
? JSON.stringify(entry)
|
|
343
|
+
: `[${entry.timestamp}] SESSION command=${entry.command} cwd=${entry.cwd} branch=${entry.gitBranch} exit=${code} duration=${duration}ms`;
|
|
344
|
+
fs.appendFileSync(logPath, line + '\n');
|
|
345
|
+
}
|
|
346
|
+
catch {
|
|
347
|
+
// Cannot do anything on exit — swallow
|
|
348
|
+
}
|
|
349
|
+
});
|
|
469
350
|
}
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
351
|
+
}
|
|
352
|
+
// ---------------------------------------------------------------------------
|
|
353
|
+
// setAuditContext
|
|
354
|
+
// ---------------------------------------------------------------------------
|
|
355
|
+
/**
|
|
356
|
+
* Merge additional metadata into the audit context.
|
|
357
|
+
* Called from CLI handlers to add worktreePath, prNumber, etc.
|
|
358
|
+
*/
|
|
359
|
+
export function setAuditContext(ctx) {
|
|
360
|
+
Object.assign(auditContext, ctx);
|
|
361
|
+
}
|
|
362
|
+
// ---------------------------------------------------------------------------
|
|
363
|
+
// parseLogLevel
|
|
364
|
+
// ---------------------------------------------------------------------------
|
|
365
|
+
/**
|
|
366
|
+
* Parse a string log level name to its numeric consola equivalent.
|
|
367
|
+
* Returns undefined for unrecognized values.
|
|
368
|
+
*/
|
|
369
|
+
export function parseLogLevel(value) {
|
|
370
|
+
const normalized = value.toLowerCase().trim();
|
|
371
|
+
const mapping = {
|
|
372
|
+
silent: -999,
|
|
373
|
+
error: 0,
|
|
374
|
+
warn: 1,
|
|
375
|
+
warning: 1,
|
|
376
|
+
info: 3,
|
|
377
|
+
debug: 4,
|
|
378
|
+
trace: 5,
|
|
379
|
+
verbose: 4,
|
|
380
|
+
};
|
|
381
|
+
return mapping[normalized];
|
|
382
|
+
}
|
|
383
|
+
// ---------------------------------------------------------------------------
|
|
384
|
+
// _resetForTesting
|
|
385
|
+
// ---------------------------------------------------------------------------
|
|
386
|
+
/**
|
|
387
|
+
* Reset all module-level state for test isolation.
|
|
388
|
+
* Prefixed with _ to signal internal-only use.
|
|
389
|
+
*/
|
|
390
|
+
export function _resetForTesting() {
|
|
391
|
+
deprecationWarned = false;
|
|
392
|
+
jsonMode = false;
|
|
393
|
+
auditContext = {};
|
|
394
|
+
auditFileWarned = false;
|
|
395
|
+
if (activeAuditReporter) {
|
|
396
|
+
activeAuditReporter.close();
|
|
397
|
+
}
|
|
398
|
+
activeAuditReporter = null;
|
|
399
|
+
// Note: we don't reset exitHandlerRegistered because process.on('exit') handlers
|
|
400
|
+
// cannot be removed cleanly and we only register once per process.
|
|
401
|
+
logger.setReporters([]);
|
|
402
|
+
logger.level = 3; // INFO default
|
|
474
403
|
}
|
|
475
404
|
//# sourceMappingURL=logger.js.map
|