chore: update dependencies and improve code formatting

- Added `vendor/discord-video-stream` to pnpm workspace.
- Refactored `llmModerationClient.ts` for better readability and consistency.
- Adjusted imports in `recordingsRoutes.ts` for clarity.
- Updated `webserver.ts` to correctly import `createRecordingsRoutes`.
- Enhanced test cases in `llmModerationClient.test.ts` for improved readability.
- Updated submodule references for `better-sqlite3`, `discord-video-stream`, `discord.js-selfbot-v13`, `drizzle-orm`, and `node-datachannel`.
- Created documentation for deprecated dependency removal plan and design.
This commit is contained in:
MythEclipse
2026-05-19 02:49:55 +07:00
parent e85967ae51
commit 7f5db953fa
12 changed files with 1384 additions and 23 deletions
@@ -0,0 +1,597 @@
# Deprecated Dependency Removal Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Remove deprecated packages from `pnpm-lock.yaml` where maintained replacements exist, and vendor/submodule only when no clean replacement exists.
**Architecture:** Treat dependency cleanup as package graph surgery: identify owner, change one dependency source at a time, reinstall, verify lockfile, then run project checks. Existing vendored workspaces stay source of truth for selfbot and discord-video-stream patches.
**Tech Stack:** Node.js, pnpm workspaces, TypeScript, Vitest, Biome, git submodules.
---
## File Structure
- Modify: `package.json` — root dependency versions, workspace references, `pnpm.onlyBuiltDependencies`, optional `pnpm.overrides` if needed.
- Modify: `pnpm-workspace.yaml` — workspace package list for any new submodule/vendor package.
- Modify: `pnpm-lock.yaml` — regenerated by `pnpm install` only.
- Modify: `vendor/discord-video-stream/package.json` — dev dependency on `discord.js-selfbot-v13` should use workspace package instead of registry.
- Modify: `vendor/discord.js-selfbot-v13/package.json` — replace deprecated `otplib@12` chain if compatible.
- Possibly create: `vendor/<package-name>` — only if package has no maintained replacement and strict cleanup still needs local patching.
## Deprecated Package Owners
Known current owners:
```text
drizzle-kit -> @esbuild-kit/esm-loader -> @esbuild-kit/core-utils
discord.js-selfbot-v13 -> otplib@12 -> @otplib/plugin-crypto, @otplib/plugin-thirty-two, @otplib/preset-default
@discordjs/opus -> @discordjs/node-pre-gyp -> npmlog, are-we-there-yet, gauge, rimraf@3, glob@7, inflight
better-sqlite3 -> prebuild-install
@lng2004/node-datachannel -> prebuild-install
```
## Task 1: Establish Baseline
**Files:**
- Read: `package.json`
- Read: `pnpm-workspace.yaml`
- Read: `vendor/discord-video-stream/package.json`
- Read: `vendor/discord.js-selfbot-v13/package.json`
- [ ] **Step 1: Capture current git state**
Run:
```bash
git status --short
```
Expected: includes existing intended changes for `package.json`, `pnpm-workspace.yaml`, `pnpm-lock.yaml`, `.gitmodules`, and `vendor/discord-video-stream`. Do not revert user changes.
- [ ] **Step 2: Capture dependency owners**
Run:
```bash
pnpm why discord.js-selfbot-v13 @esbuild-kit/core-utils @esbuild-kit/esm-loader @otplib/plugin-crypto @otplib/plugin-thirty-two @otplib/preset-default are-we-there-yet fs-then-native gauge inflight lodash.pick npmlog prebuild-install stream-connect test-value
```
Expected: output maps deprecated packages to direct owners. Save relevant owner names in notes for next tasks.
- [ ] **Step 3: Capture install warning baseline**
Run:
```bash
pnpm install
```
Expected: install completes. Warnings may mention deprecated transitive packages.
- [ ] **Step 4: Capture baseline checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass before dependency changes. If failing, stop and report exact failures before continuing.
## Task 2: Patch `discord-video-stream` Workspace Selfbot Reference
**Files:**
- Modify: `vendor/discord-video-stream/package.json`
- Modify: `pnpm-lock.yaml`
- [ ] **Step 1: Inspect current devDependency**
Open `vendor/discord-video-stream/package.json` and find:
```json
"discord.js-selfbot-v13": "^3.7.1"
```
Expected: exists under `devDependencies`.
- [ ] **Step 2: Replace devDependency with workspace reference**
Change that entry to:
```json
"discord.js-selfbot-v13": "workspace:*"
```
Keep peer dependency unchanged:
```json
"peerDependencies": {
"discord.js-selfbot-v13": "^3.6.0"
}
```
- [ ] **Step 3: Reinstall**
Run:
```bash
pnpm install
```
Expected: lockfile uses local workspace for `discord.js-selfbot-v13` in `vendor/discord-video-stream` importer.
- [ ] **Step 4: Verify no registry selfbot fetch from discord-video-stream**
Run:
```bash
pnpm why discord.js-selfbot-v13
```
Expected: root and discord-video-stream both point to `link:vendor/discord.js-selfbot-v13` or workspace link.
- [ ] **Step 5: Run checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass.
## Task 3: Upgrade or Patch `otplib` Chain in Vendored Selfbot
**Files:**
- Modify: `vendor/discord.js-selfbot-v13/package.json`
- Modify: `pnpm-lock.yaml`
- [ ] **Step 1: Locate selfbot otplib dependency**
Run:
```bash
node -e "const p=require('./vendor/discord.js-selfbot-v13/package.json'); console.log(p.dependencies?.otplib || p.devDependencies?.otplib)"
```
Expected: prints `^12.x` or `12.x`.
- [ ] **Step 2: Check latest otplib version**
Run:
```bash
npm view otplib version deprecated --json
```
Expected: latest version is not deprecated.
- [ ] **Step 3: Change selfbot dependency to otplib latest major**
In `vendor/discord.js-selfbot-v13/package.json`, replace existing `otplib` dependency value with latest non-deprecated major range. Example if latest is 13.x:
```json
"otplib": "^13.0.0"
```
Do not change package name or selfbot exports.
- [ ] **Step 4: Reinstall**
Run:
```bash
pnpm install
```
Expected: install completes. `@otplib/plugin-crypto`, `@otplib/plugin-thirty-two`, and `@otplib/preset-default` should disappear if otplib v13 no longer pulls them.
- [ ] **Step 5: Verify otplib deprecated plugins gone**
Run:
```bash
pnpm why @otplib/plugin-crypto @otplib/plugin-thirty-two @otplib/preset-default
```
Expected: no dependency path for those packages. If they remain under `otplib`, inspect latest otplib metadata and stop before vendoring otplib.
- [ ] **Step 6: Run checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass. If selfbot code breaks due otplib API changes, revert only the otplib version change and report API mismatch.
## Task 4: Upgrade `drizzle-kit` to Remove `@esbuild-kit/*`
**Files:**
- Modify: `package.json`
- Modify: `pnpm-lock.yaml`
- [ ] **Step 1: Check current and latest drizzle-kit**
Run:
```bash
node -e "const p=require('./package.json'); console.log(p.devDependencies['drizzle-kit'])"
npm view drizzle-kit version deprecated --json
```
Expected: latest version is not deprecated.
- [ ] **Step 2: Update root devDependency if newer version exists**
If latest version is newer than `0.31.10`, change `package.json` devDependency:
```json
"drizzle-kit": "^<latest-version>"
```
Example:
```json
"drizzle-kit": "^0.32.0"
```
Use actual latest version from npm output.
- [ ] **Step 3: Reinstall**
Run:
```bash
pnpm install
```
Expected: install completes.
- [ ] **Step 4: Verify esbuild-kit packages gone**
Run:
```bash
pnpm why @esbuild-kit/core-utils @esbuild-kit/esm-loader
```
Expected: no dependency path. If latest `drizzle-kit` still pulls them, keep latest only if project checks pass; do not vendor `drizzle-kit` unless user confirms dev-only strictness.
- [ ] **Step 5: Run drizzle commands**
Run:
```bash
pnpm run db:generate -- --help
pnpm run db:migrate -- --help
```
Expected: commands print help or usage without crashing. Do not run actual migrations.
- [ ] **Step 6: Run checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass.
## Task 5: Remove or Replace Direct `@discordjs/opus`
**Files:**
- Modify: `package.json`
- Modify: `pnpm-lock.yaml`
- Inspect: `src/recorder/decoder.ts`
- Inspect: `tests/decoder.test.ts`
- [ ] **Step 1: Find project usage of `@discordjs/opus`**
Run:
```bash
grep -R "@discordjs/opus\|OpusEncoder\|OpusDecoder" -n src tests package.json
```
Expected: usage locations show whether direct package is imported by project code or only required indirectly by `prism-media`.
- [ ] **Step 2: Check `@discordjs/voice` encryption/audio requirements**
Run:
```bash
pnpm why @discordjs/opus prism-media
```
Expected: shows `@discordjs/voice` and root dependency relationships.
- [ ] **Step 3: Test removal in package manifest**
Remove root dependency line from `package.json`:
```json
"@discordjs/opus": "^0.10.0",
```
Do not edit code yet.
- [ ] **Step 4: Reinstall**
Run:
```bash
pnpm install
```
Expected: install completes. If install or peer resolution fails, restore `@discordjs/opus` and continue to Step 8.
- [ ] **Step 5: Run decoder-specific tests**
Run:
```bash
pnpm run test -- tests/decoder.test.ts
```
Expected: tests pass or skip native opus gracefully. If tests fail because native opus is required by project behavior, restore `@discordjs/opus` and continue to Step 8.
- [ ] **Step 6: Run full checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass.
- [ ] **Step 7: Verify deprecated node-pre-gyp chain gone**
Run:
```bash
pnpm why @discordjs/node-pre-gyp npmlog are-we-there-yet gauge rimraf glob inflight
```
Expected: no dependency path through `@discordjs/opus`. If gone, task complete.
- [ ] **Step 8: If `@discordjs/opus` is required, try maintained alternatives**
Run:
```bash
npm view opusscript version deprecated --json
npm view @evan/opus version deprecated --json
```
Expected: identify non-deprecated candidate. Do not switch unless package supports same runtime path used by `prism-media` or direct project imports.
- [ ] **Step 9: If no compatible maintained alternative exists, vendor decision checkpoint**
Stop and report:
```text
@discordjs/opus still required. No compatible maintained replacement verified. Next action requires cloning/vendoring smallest package owner or accepting deprecated native install chain.
```
Do not clone without user confirmation of target repository.
## Task 6: Evaluate `prebuild-install` Owners
**Files:**
- Modify: `package.json` only if safe upgrade exists
- Modify: `vendor/discord-video-stream/package.json` only if safe upgrade exists
- Modify: `pnpm-lock.yaml`
- [ ] **Step 1: Check owner versions**
Run:
```bash
npm view better-sqlite3 version deprecated --json
npm view @lng2004/node-datachannel version deprecated --json
node -e "const root=require('./package.json'); const dvs=require('./vendor/discord-video-stream/package.json'); console.log({betterSqlite3: root.dependencies['better-sqlite3'], nodeDatachannel: dvs.dependencies['@lng2004/node-datachannel']})"
```
Expected: latest versions known.
- [ ] **Step 2: Upgrade `better-sqlite3` if newer version exists**
If latest is newer than current, change `package.json`:
```json
"better-sqlite3": "^<latest-version>"
```
Use actual latest version.
- [ ] **Step 3: Upgrade `@lng2004/node-datachannel` if newer version exists**
If latest is newer than current and package name still matches discord-video-stream requirements, change `vendor/discord-video-stream/package.json`:
```json
"@lng2004/node-datachannel": "<latest-version>"
```
Use exact latest version only if upstream uses exact published builds.
- [ ] **Step 4: Reinstall**
Run:
```bash
pnpm install
```
Expected: native dependencies install or reuse existing builds successfully.
- [ ] **Step 5: Verify `prebuild-install` status**
Run:
```bash
pnpm why prebuild-install
```
Expected: either no dependency path, or only native owners remain.
- [ ] **Step 6: Run checks**
Run:
```bash
pnpm run typecheck
pnpm run test
```
Expected: both pass.
- [ ] **Step 7: If `prebuild-install` remains**
Stop and report owner paths. Do not vendor native packages unless no maintained version removes it and user explicitly wants native submodule maintenance.
## Task 7: Final Deprecation Audit
**Files:**
- Modify: `pnpm-lock.yaml` through install only
- [ ] **Step 1: Run clean install audit**
Run:
```bash
pnpm install
```
Expected: no deprecated package warnings where maintained replacements were applied.
- [ ] **Step 2: Run owner query for all known deprecated names**
Run:
```bash
pnpm why discord.js-selfbot-v13 @esbuild-kit/core-utils @esbuild-kit/esm-loader @otplib/plugin-crypto @otplib/plugin-thirty-two @otplib/preset-default are-we-there-yet fs-then-native gauge inflight lodash.pick npmlog prebuild-install stream-connect test-value @discordjs/node-pre-gyp
```
Expected: no paths for packages removed by prior tasks. Remaining paths must be only approved unavoidable native/package-owner cases.
- [ ] **Step 3: Check npm deprecation metadata for remaining suspect packages**
Run:
```bash
node - <<'NODE'
const {execFileSync}=require('child_process');
const pkgs=['discord.js-selfbot-v13','@esbuild-kit/core-utils','@esbuild-kit/esm-loader','@otplib/plugin-crypto','@otplib/plugin-thirty-two','@otplib/preset-default','are-we-there-yet','fs-then-native','gauge','inflight','lodash.pick','npmlog','prebuild-install','stream-connect','test-value','@discordjs/node-pre-gyp'];
for (const p of pkgs) {
try {
const out=execFileSync('npm',['view',p,'deprecated','--json'],{encoding:'utf8'}).trim();
if (out && out !== 'null') console.log(`${p}: ${JSON.parse(out)}`);
} catch {
console.log(`${p}: npm view failed`);
}
}
NODE
```
Expected: command prints metadata only. Compare printed package names with `pnpm why` output.
- [ ] **Step 4: Run final checks**
Run:
```bash
pnpm run typecheck
pnpm run test
pnpm run lint
```
Expected: all pass.
## Task 8: Document Remaining Unavoidable Deprecated Packages
**Files:**
- Modify: `docs/superpowers/specs/2026-05-19-deprecated-dependency-removal-design.md`
- [ ] **Step 1: If no deprecated packages remain, append success note**
Append this section:
```markdown
## Final Audit Result
All known deprecated packages from the initial audit were removed from the active pnpm dependency graph.
```
- [ ] **Step 2: If deprecated packages remain, append owner note**
Append this section with actual owner paths from `pnpm why`:
```markdown
## Final Audit Result
Remaining deprecated packages after maintained upgrade attempts:
- `<package>` remains via `<owner path>`. Reason: `<no maintained replacement found | native install chain still used by latest upstream | user-approved vendored package>`.
These are candidates for future vendoring/submodule patching if strict lockfile cleanup remains required.
```
- [ ] **Step 3: Run final git diff review**
Run:
```bash
git diff -- package.json pnpm-workspace.yaml pnpm-lock.yaml vendor/discord-video-stream/package.json vendor/discord.js-selfbot-v13/package.json docs/superpowers/specs/2026-05-19-deprecated-dependency-removal-design.md
```
Expected: diff contains only dependency migration changes and audit note.
## Task 9: Commit Checkpoint Only If User Requests Commit
**Files:**
- Stage only changed package/spec/submodule files relevant to dependency cleanup.
- [ ] **Step 1: Show status**
Run:
```bash
git status --short
```
Expected: changed files match work done.
- [ ] **Step 2: Ask before committing**
Ask user whether to commit. Do not commit unless explicitly requested.
- [ ] **Step 3: If user asks to commit, create commit**
Use exact changed file list, not `git add -A`. Commit message:
```bash
git commit -m "$(cat <<'EOF'
chore: remove deprecated dependency graph entries
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
EOF
)"
```
Expected: commit succeeds without bypassing hooks.
## Self-Review
- Spec coverage: plan covers replace-first, workspace patches, submodule/vendor checkpoint, verification, and final audit documentation.
- Placeholder scan: no TBD/TODO placeholders; steps include exact commands and expected outcomes.
- Type consistency: paths and package names match current workspace files and dependency owners.
@@ -0,0 +1,652 @@
# Winston Logging Refactor Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** Replace Pino logging with Winston, normalize log levels, and write readable console plus JSON file logs without changing application behavior.
**Architecture:** Keep `src/logger.ts` as the single logging entry point. Rebuild it around one Winston logger with child context support, centralized metadata/error serialization, colorized console output, and JSON file transports. Keep call-site changes minimal and only adjust imports/usages that fail after the backend swap.
**Tech Stack:** TypeScript, Node.js ESM, Winston, Zod config validation, pnpm, Vitest, Biome.
---
## File Structure
- Modify: `package.json` — replace Pino dependencies with Winston.
- Modify: `pnpm-lock.yaml` — update via pnpm after dependency changes.
- Modify: `src/config.ts` — expand `LOG_LEVEL` enum to Winston npm levels.
- Modify: `src/logger.ts` — replace Pino implementation with Winston implementation.
- Modify: `.gitignore` — ignore runtime `logs/` directory.
- Test: `tests/logger.test.ts` — add focused logger behavior tests for level validation, error serialization, and file output format helpers.
- Inspect and modify only if needed: files importing `createChildLogger` or `logger`.
## Important Git Note
The prior design-spec commit unexpectedly included vendor submodule entries that were already staged in the working tree. Before making implementation commits, run `git status --short` and do not stage unrelated vendor or existing user changes. Stage only files touched by this plan.
---
### Task 1: Add Logger Tests Before Migration
**Files:**
- Create: `tests/logger.test.ts`
- Modify: none
- [ ] **Step 1: Write tests for Winston-compatible logger behavior**
Create `tests/logger.test.ts` with this content:
```ts
import { describe, expect, it } from "vitest";
import { formatLogMetadataForTest, serializeLogValueForTest } from "../src/logger";
class TestError extends Error {
public code = "TEST_CODE";
public statusCode = 418;
constructor() {
super("test failure");
this.name = "TestError";
}
}
describe("logger serialization", () => {
it("serializes Error values with stable fields", () => {
const serialized = serializeLogValueForTest(new TestError());
expect(serialized).toMatchObject({
name: "TestError",
message: "test failure",
code: "TEST_CODE",
statusCode: 418,
});
expect(serialized).toHaveProperty("stack");
});
it("serializes nested error metadata keys", () => {
const error = new TestError();
expect(formatLogMetadataForTest({ error, err: error, reason: error })).toMatchObject({
error: {
name: "TestError",
message: "test failure",
code: "TEST_CODE",
statusCode: 418,
},
err: {
name: "TestError",
message: "test failure",
code: "TEST_CODE",
statusCode: 418,
},
reason: {
name: "TestError",
message: "test failure",
code: "TEST_CODE",
statusCode: 418,
},
});
});
it("preserves plain metadata", () => {
expect(
formatLogMetadataForTest({ context: "bot", signal: "SIGINT", count: 2 }),
).toEqual({ context: "bot", signal: "SIGINT", count: 2 });
});
});
```
- [ ] **Step 2: Run test to verify current implementation fails**
Run:
```bash
pnpm vitest run tests/logger.test.ts
```
Expected: FAIL because `formatLogMetadataForTest` and `serializeLogValueForTest` are not exported from `src/logger.ts` yet.
- [ ] **Step 3: Commit failing tests only**
```bash
git add tests/logger.test.ts
git commit -m "test: cover logger metadata serialization"
```
Expected: commit contains only `tests/logger.test.ts`.
---
### Task 2: Update Dependencies and Config Schema
**Files:**
- Modify: `package.json`
- Modify: `pnpm-lock.yaml`
- Modify: `src/config.ts`
- [ ] **Step 1: Update package dependencies**
Run:
```bash
pnpm remove pino pino-http pino-pretty
pnpm add winston
```
Expected:
- `package.json` dependencies no longer include `pino` or `pino-http`.
- `package.json` devDependencies no longer include `pino-pretty`.
- `package.json` dependencies include `winston`.
- `pnpm-lock.yaml` updates accordingly.
- [ ] **Step 2: Expand `LOG_LEVEL` validation**
In `src/config.ts`, replace the current `LOG_LEVEL` line:
```ts
LOG_LEVEL: z.enum(["debug", "info", "warn", "error"]).default("info"),
```
with:
```ts
LOG_LEVEL: z
.enum(["error", "warn", "info", "http", "verbose", "debug", "silly"])
.default("info"),
```
- [ ] **Step 3: Run targeted config tests**
Run:
```bash
pnpm vitest run tests/config.test.ts
```
Expected: PASS.
- [ ] **Step 4: Commit dependency and config changes**
```bash
git add package.json pnpm-lock.yaml src/config.ts
git commit -m "chore: switch logging dependency to winston"
```
Expected: commit contains only dependency files and `src/config.ts`.
---
### Task 3: Replace `src/logger.ts` With Winston Implementation
**Files:**
- Modify: `src/logger.ts`
- [ ] **Step 1: Replace logger implementation**
Replace entire `src/logger.ts` with:
```ts
import fs from "node:fs";
import path from "node:path";
import winston from "winston";
const isDev = process.env.NODE_ENV !== "production";
const logLevel = process.env.LOG_LEVEL || (isDev ? "debug" : "info");
const logsDir = path.resolve(process.cwd(), "logs");
fs.mkdirSync(logsDir, { recursive: true });
type LogMetadata = Record<string, unknown>;
type SerializedError = {
name: string;
message: string;
stack?: string;
code?: unknown;
statusCode?: unknown;
} & Record<string, unknown>;
const serializeError = (error: Error): SerializedError => {
const serialized: SerializedError = {
name: error.name,
message: error.message,
};
if (error.stack) {
serialized.stack = error.stack;
}
const errorWithFields = error as Error & {
code?: unknown;
statusCode?: unknown;
[key: string]: unknown;
};
if (errorWithFields.code !== undefined) {
serialized.code = errorWithFields.code;
}
if (errorWithFields.statusCode !== undefined) {
serialized.statusCode = errorWithFields.statusCode;
}
for (const [key, value] of Object.entries(errorWithFields)) {
if (serialized[key] === undefined) {
serialized[key] = value;
}
}
return serialized;
};
const serializeLogValue = (value: unknown): unknown => {
if (value instanceof Error) {
return serializeError(value);
}
if (Array.isArray(value)) {
return value.map(serializeLogValue);
}
if (value && typeof value === "object") {
return Object.fromEntries(
Object.entries(value).map(([key, nestedValue]) => [
key,
serializeLogValue(nestedValue),
]),
);
}
return value;
};
const formatLogMetadata = (metadata: LogMetadata): LogMetadata => {
return Object.fromEntries(
Object.entries(metadata).map(([key, value]) => [key, serializeLogValue(value)]),
);
};
const metadataFormat = winston.format((info) => {
const { level, message, timestamp, ...metadata } = info;
return {
level,
message,
timestamp,
...formatLogMetadata(metadata),
};
});
const consoleFormat = winston.format.printf((info) => {
const { level, message, timestamp, context, ...metadata } = info;
const contextLabel = context ? ` [${String(context)}]` : "";
const metadataText = Object.keys(metadata).length
? ` ${JSON.stringify(metadata)}`
: "";
return `${timestamp} ${level}${contextLabel}: ${message}${metadataText}`;
});
export const logger = winston.createLogger({
level: logLevel,
levels: winston.config.npm.levels,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.errors({ stack: true }),
metadataFormat(),
),
transports: [
new winston.transports.Console({
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
metadataFormat(),
consoleFormat,
),
}),
new winston.transports.File({
filename: path.join(logsDir, "app.log"),
format: winston.format.json(),
}),
new winston.transports.File({
filename: path.join(logsDir, "error.log"),
level: "error",
format: winston.format.json(),
}),
],
});
export const createChildLogger = (context: string) => {
return logger.child({ context });
};
export const serializeLogValueForTest = serializeLogValue;
export const formatLogMetadataForTest = formatLogMetadata;
```
- [ ] **Step 2: Run logger tests**
Run:
```bash
pnpm vitest run tests/logger.test.ts
```
Expected: PASS.
- [ ] **Step 3: Run TypeScript check**
Run:
```bash
pnpm run typecheck
```
Expected: either PASS or type errors only from logger call sites needing Winston-compatible adjustments.
- [ ] **Step 4: Commit Winston logger implementation**
If typecheck passes:
```bash
git add src/logger.ts
git commit -m "refactor: replace pino logger with winston"
```
Expected: commit contains only `src/logger.ts`.
If typecheck fails in logger call sites, do not commit yet. Continue to Task 4, then commit `src/logger.ts` together with required call-site fixes.
---
### Task 4: Fix Logger Call Sites Only If Needed
**Files:**
- Inspect: `src/**/*.ts`
- Modify only files with TypeScript errors from Task 3.
- [ ] **Step 1: Find logger imports and usages**
Run:
```bash
grep -rn "createChildLogger\|from \"./logger\"\|from \"../logger\"\|logger\.\|log\." src --include="*.ts"
```
Expected: list of logger imports and method calls.
- [ ] **Step 2: Fix pino-style calls if TypeScript requires it**
Use this mapping only where needed:
Before:
```ts
logger.info({ signal }, "Graceful shutdown initiated");
```
After:
```ts
logger.info("Graceful shutdown initiated", { signal });
```
Before:
```ts
logger.error({ error }, "Failed to initialize app");
```
After:
```ts
logger.error("Failed to initialize app", { error });
```
Before:
```ts
logger.warn({ error }, "Backlog sync failed");
```
After:
```ts
logger.warn("Backlog sync failed", { error });
```
Plain string calls remain unchanged:
```ts
logger.info("Creating Discord client");
logger.debug("Queue empty, no playback started");
```
- [ ] **Step 3: Re-run TypeScript check**
Run:
```bash
pnpm run typecheck
```
Expected: PASS.
- [ ] **Step 4: Commit call-site fixes**
```bash
git add src/logger.ts src/index.ts src/webserver.ts src/middleware.ts src/voiceController.ts src/media/mediaController.ts src/media/screenShareController.ts src/moderation/broadcaster.ts src/moderation/messageCapture.ts src/streaming/transcoder.ts
git commit -m "refactor: normalize logger call sites for winston"
```
Expected: commit stages only files actually changed. If some listed files are unchanged, `git add` is harmless.
---
### Task 5: Ignore Runtime Log Files
**Files:**
- Modify: `.gitignore`
- [ ] **Step 1: Check whether `logs/` is already ignored**
Run:
```bash
grep -n "^logs/$" .gitignore || true
```
Expected: either one matching line or no output.
- [ ] **Step 2: Add `logs/` if missing**
If no output from Step 1, append:
```gitignore
logs/
```
- [ ] **Step 3: Verify ignore rule**
Run:
```bash
git check-ignore logs/app.log
```
Expected output:
```text
logs/app.log
```
- [ ] **Step 4: Commit ignore rule**
```bash
git add .gitignore
git commit -m "chore: ignore runtime log files"
```
Expected: commit contains only `.gitignore`. If `.gitignore` already ignored `logs/`, skip this commit.
---
### Task 6: Verify No Pino Usage Remains
**Files:**
- Inspect: repository source and package files
- [ ] **Step 1: Search for Pino imports/usages**
Run:
```bash
grep -rn "pino\|pino-http\|pino-pretty" src package.json pnpm-lock.yaml --exclude-dir=node_modules || true
```
Expected: no output. If output appears only in old comments/docs outside runtime files, remove or update those comments/docs if they are part of the changed scope.
- [ ] **Step 2: Search for Winston dependency**
Run:
```bash
grep -n '"winston"' package.json
```
Expected output contains one `winston` dependency line.
- [ ] **Step 3: Run focused tests**
Run:
```bash
pnpm vitest run tests/logger.test.ts tests/config.test.ts
```
Expected: PASS.
- [ ] **Step 4: Commit cleanup if any files changed**
```bash
git status --short
git add <only-files-changed-by-this-task>
git commit -m "chore: remove remaining pino references"
```
Expected: commit only if Step 1 required changes.
---
### Task 7: Runtime Log Output Verification
**Files:**
- No source changes expected
- [ ] **Step 1: Run one-shot logger command**
Run:
```bash
node --import tsx -e 'import { logger } from "./src/logger.ts"; logger.info("logger smoke info", { context: "smoke", value: 1 }); logger.error("logger smoke error", { error: new Error("smoke failure") }); await new Promise((resolve) => setTimeout(resolve, 250));'
```
Expected:
- Console shows readable timestamped logs.
- Info line includes `smoke` context metadata.
- Error line includes serialized error metadata.
- [ ] **Step 2: Verify app log file exists and contains JSON**
Run:
```bash
test -s logs/app.log && node -e 'const fs = require("fs"); const line = fs.readFileSync("logs/app.log", "utf8").trim().split("\n").at(-1); const parsed = JSON.parse(line); if (!parsed.level || !parsed.message) process.exit(1); console.log(parsed.level + ":" + parsed.message);'
```
Expected output includes:
```text
error:logger smoke error
```
- [ ] **Step 3: Verify error log file exists and contains error JSON**
Run:
```bash
test -s logs/error.log && node -e 'const fs = require("fs"); const line = fs.readFileSync("logs/error.log", "utf8").trim().split("\n").at(-1); const parsed = JSON.parse(line); if (parsed.level !== "error") process.exit(1); console.log(parsed.level + ":" + parsed.message);'
```
Expected output:
```text
error:logger smoke error
```
---
### Task 8: Full Validation
**Files:**
- No source changes expected unless validation reveals issues.
- [ ] **Step 1: Run typecheck**
Run:
```bash
pnpm run typecheck
```
Expected: PASS.
- [ ] **Step 2: Run test suite**
Run:
```bash
pnpm run test
```
Expected: PASS.
- [ ] **Step 3: Run lint**
Run:
```bash
pnpm run lint
```
Expected: PASS.
- [ ] **Step 4: Inspect final git status**
Run:
```bash
git status --short
```
Expected: no uncommitted changes from this plan except ignored `logs/` files. Existing unrelated user changes may still appear; do not stage them.
- [ ] **Step 5: Final commit if validation fixes were needed**
If validation required fixes:
```bash
git add <only-files-changed-by-validation-fixes>
git commit -m "fix: stabilize winston logging migration"
```
Expected: commit contains only validation fixes.
---
## Self-Review Notes
- Spec coverage: dependency swap, `LOG_LEVEL`, centralized logger, console/file output, error serialization, Pino removal, and validation all have tasks.
- Placeholder scan: no TBD/TODO/fill-in placeholders remain; `<only-files-changed-by-this-task>` and similar are explicit safety instructions to avoid staging unrelated user changes.
- Type consistency: test helper names match `src/logger.ts` exports; Winston npm levels match `src/config.ts`; file names match spec.