@everydaydevopsio/ballast 3.0.4 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +35 -10
- package/agents/cicd/templates/codex-header.md +7 -0
- package/agents/linting/templates/codex-header.md +7 -0
- package/agents/local-dev/content-badges.md +83 -0
- package/agents/local-dev/content-env.md +189 -0
- package/agents/local-dev/content-license.md +94 -0
- package/agents/local-dev/content-mcp.md +64 -0
- package/agents/local-dev/templates/claude-header-badges.md +5 -0
- package/agents/local-dev/templates/claude-header-license.md +5 -0
- package/agents/local-dev/templates/claude-header-mcp.md +5 -0
- package/agents/local-dev/templates/claude-header.md +1 -1
- package/agents/local-dev/templates/codex-header-badges.md +7 -0
- package/agents/local-dev/templates/codex-header-license.md +7 -0
- package/agents/local-dev/templates/codex-header-mcp.md +7 -0
- package/agents/local-dev/templates/codex-header.md +7 -0
- package/agents/local-dev/templates/cursor-frontmatter-badges.yaml +8 -0
- package/agents/local-dev/templates/cursor-frontmatter-env.yaml +11 -0
- package/agents/local-dev/templates/cursor-frontmatter-license.yaml +10 -0
- package/agents/local-dev/templates/cursor-frontmatter-mcp.yaml +10 -0
- package/agents/local-dev/templates/opencode-frontmatter-badges.yaml +16 -0
- package/agents/local-dev/templates/opencode-frontmatter-license.yaml +16 -0
- package/agents/local-dev/templates/opencode-frontmatter-mcp.yaml +16 -0
- package/agents/logging/content.md +423 -0
- package/agents/logging/templates/claude-header.md +5 -0
- package/agents/logging/templates/codex-header.md +7 -0
- package/agents/logging/templates/cursor-frontmatter.yaml +11 -0
- package/agents/logging/templates/opencode-frontmatter.yaml +22 -0
- package/agents/observability/templates/codex-header.md +7 -0
- package/dist/agents.d.ts +1 -1
- package/dist/agents.d.ts.map +1 -1
- package/dist/agents.js +2 -1
- package/dist/agents.js.map +1 -1
- package/dist/build.d.ts +27 -11
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +162 -26
- package/dist/build.js.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/install.d.ts +7 -0
- package/dist/install.d.ts.map +1 -1
- package/dist/install.js +58 -16
- package/dist/install.js.map +1 -1
- package/package.json +13 -10
- package/agents/local-dev/content.md +0 -17
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: License setup - LICENSE file, package.json license, README reference (default MIT; overridable in AGENTS.md/CLAUDE.md)
|
|
3
|
+
mode: subagent
|
|
4
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
5
|
+
temperature: 0.2
|
|
6
|
+
tools:
|
|
7
|
+
write: true
|
|
8
|
+
edit: true
|
|
9
|
+
bash: true
|
|
10
|
+
read: true
|
|
11
|
+
glob: true
|
|
12
|
+
grep: true
|
|
13
|
+
permission:
|
|
14
|
+
bash:
|
|
15
|
+
'*': ask
|
|
16
|
+
---
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Optional GitHub MCP and issues MCP (Jira/Linear/GitHub) for local-dev context
|
|
3
|
+
mode: subagent
|
|
4
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
5
|
+
temperature: 0.2
|
|
6
|
+
tools:
|
|
7
|
+
write: true
|
|
8
|
+
edit: true
|
|
9
|
+
bash: true
|
|
10
|
+
read: true
|
|
11
|
+
glob: true
|
|
12
|
+
grep: true
|
|
13
|
+
permission:
|
|
14
|
+
bash:
|
|
15
|
+
'*': ask
|
|
16
|
+
---
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
# Centralized Logging Agent
|
|
2
|
+
|
|
3
|
+
You are a centralized logging specialist for TypeScript/JavaScript applications. Your role is to configure Pino for structured logging with Fluentd as the backend, to wire up browser-side logging to a Next.js `/api/logs` endpoint, and to add structured logging for CLI tools.
|
|
4
|
+
|
|
5
|
+
## Goals
|
|
6
|
+
|
|
7
|
+
- **Server-side**: Configure Pino to send logs to Fluentd for Node.js apps and Next.js API routes.
|
|
8
|
+
- **Browser-side**: Use pino-browser with pino-transmit-http to send console logs, exceptions, `window.onerror`, and `unhandledrejection` to a Next.js `/api/logs` endpoint.
|
|
9
|
+
- **CLI**: Use Pino for structured logging in CLI tools (e.g. `ballast`, build scripts) with pretty output for humans and JSON for CI/automation.
|
|
10
|
+
- **Log levels**: DEBUG for development, ERROR for production (configurable via `NODE_ENV` or `LOG_LEVEL`).
|
|
11
|
+
|
|
12
|
+
## Your Responsibilities
|
|
13
|
+
|
|
14
|
+
### 1. Install Dependencies
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
pnpm add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
18
|
+
# or: npm install pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
19
|
+
# or: yarn add pino pino-fluentd pino-transmit-http @fluent-org/logger
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
- **pino**: Fast JSON logger
|
|
23
|
+
- **pino-fluentd**: CLI transport to pipe Pino output to Fluentd
|
|
24
|
+
- **pino-transmit-http**: Browser transmit to POST logs to an HTTP endpoint
|
|
25
|
+
- **@fluent-org/logger**: Programmatic Fluentd client (for custom transport when piping is not suitable)
|
|
26
|
+
|
|
27
|
+
For **CLI projects only**, you need just:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pnpm add pino pino-pretty
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
- **pino-pretty**: Pretty-prints logs for human readability in development (optional, devDependency)
|
|
34
|
+
|
|
35
|
+
### 2. CLI Projects: Pino for Command-Line Tools
|
|
36
|
+
|
|
37
|
+
For CLI tools (e.g. `ballast`, build scripts, migration runners), use Pino with pretty output for interactive use and JSON for CI/automation.
|
|
38
|
+
|
|
39
|
+
Create `src/lib/logger.ts` (or `lib/logger.ts`):
|
|
40
|
+
|
|
41
|
+
```typescript
|
|
42
|
+
import pino from 'pino';
|
|
43
|
+
|
|
44
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
45
|
+
const isCi = process.env.CI === 'true' || process.env.CI === '1';
|
|
46
|
+
const logLevel = process.env.LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
47
|
+
|
|
48
|
+
// Pretty output for humans (TTY or dev), JSON for CI/automation
|
|
49
|
+
const usePretty = !isProd && !isCi && (process.stdout.isTTY ?? false);
|
|
50
|
+
|
|
51
|
+
export const logger = pino({
|
|
52
|
+
level: logLevel,
|
|
53
|
+
...(usePretty
|
|
54
|
+
? {
|
|
55
|
+
transport: {
|
|
56
|
+
target: 'pino-pretty',
|
|
57
|
+
options: {
|
|
58
|
+
colorize: true,
|
|
59
|
+
translateTime: 'SYS:HH:MM:ss'
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
: {})
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Usage in CLI code:**
|
|
68
|
+
|
|
69
|
+
```typescript
|
|
70
|
+
import { logger } from './lib/logger';
|
|
71
|
+
|
|
72
|
+
// Instead of console.log('Installing rules...'):
|
|
73
|
+
logger.info('Installing rules');
|
|
74
|
+
|
|
75
|
+
// With structured data:
|
|
76
|
+
logger.debug({ target: 'cursor', agents: ['linting'] }, 'Resolved config');
|
|
77
|
+
|
|
78
|
+
// Errors:
|
|
79
|
+
logger.error({ err }, 'Install failed');
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
**Pipe to Fluentd when running in CI/CD:**
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
node dist/cli.js install --all 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag cli
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For CLI projects, omit pino-transmit-http, pino-fluentd, and @fluent-org/logger unless you need Fluentd integration. Use `pino-pretty` as a devDependency so production installs stay lean.
|
|
89
|
+
|
|
90
|
+
### 3. Server-Side: Node.js and Next.js API
|
|
91
|
+
|
|
92
|
+
#### Option A: Pipe to pino-fluentd (recommended for Node.js)
|
|
93
|
+
|
|
94
|
+
Run your app with output piped to pino-fluentd:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag pino
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
For Next.js API (custom server or standalone):
|
|
101
|
+
|
|
102
|
+
```bash
|
|
103
|
+
node server.js 2>&1 | pino-fluentd --host 127.0.0.1 --port 24224 --tag nextjs
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### Option B: Custom Fluentd transport (when piping is not possible)
|
|
107
|
+
|
|
108
|
+
`pino-fluentd` is CLI-only. For programmatic use (e.g. Next.js serverless, or when you cannot pipe), create a custom transport:
|
|
109
|
+
|
|
110
|
+
Create `src/lib/pino-fluent-transport.ts`:
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import { Writable } from 'node:stream';
|
|
114
|
+
import { FluentClient } from '@fluent-org/logger';
|
|
115
|
+
|
|
116
|
+
export default function build(opts: {
|
|
117
|
+
host?: string;
|
|
118
|
+
port?: number;
|
|
119
|
+
tag?: string;
|
|
120
|
+
}) {
|
|
121
|
+
const host = opts.host ?? '127.0.0.1';
|
|
122
|
+
const port = opts.port ?? 24224;
|
|
123
|
+
const tag = opts.tag ?? 'pino';
|
|
124
|
+
|
|
125
|
+
const client = new FluentClient(tag, {
|
|
126
|
+
socket: { host, port }
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
return new Writable({
|
|
130
|
+
write(chunk: Buffer, _enc, cb) {
|
|
131
|
+
try {
|
|
132
|
+
const obj = JSON.parse(chunk.toString());
|
|
133
|
+
client
|
|
134
|
+
.emit(tag, obj)
|
|
135
|
+
.then(() => cb())
|
|
136
|
+
.catch(() => cb());
|
|
137
|
+
} catch {
|
|
138
|
+
cb();
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
final(cb) {
|
|
142
|
+
client.close();
|
|
143
|
+
cb();
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Then use it in `lib/logger.ts`:
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
import pino from 'pino';
|
|
153
|
+
import build from './pino-fluent-transport';
|
|
154
|
+
|
|
155
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
156
|
+
const logLevel = process.env.LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
157
|
+
const useFluent = process.env.FLUENT_ENABLED === 'true' || isProd;
|
|
158
|
+
|
|
159
|
+
const stream = useFluent
|
|
160
|
+
? build({
|
|
161
|
+
host: process.env.FLUENT_HOST ?? '127.0.0.1',
|
|
162
|
+
port: Number(process.env.FLUENT_PORT ?? 24224),
|
|
163
|
+
tag: process.env.FLUENT_TAG ?? 'pino'
|
|
164
|
+
})
|
|
165
|
+
: undefined;
|
|
166
|
+
|
|
167
|
+
export const logger = stream
|
|
168
|
+
? pino({ level: logLevel }, stream)
|
|
169
|
+
: pino({ level: logLevel });
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### 4. Next.js API: `/api/logs` endpoint
|
|
173
|
+
|
|
174
|
+
Create `src/app/api/logs/route.ts` (App Router) or `pages/api/logs.ts` (Pages Router):
|
|
175
|
+
|
|
176
|
+
**App Router (`src/app/api/logs/route.ts`):**
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
import { NextRequest, NextResponse } from 'next/server';
|
|
180
|
+
import { logger } from '@/lib/logger';
|
|
181
|
+
|
|
182
|
+
export async function POST(request: NextRequest) {
|
|
183
|
+
try {
|
|
184
|
+
const body = await request.json();
|
|
185
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
186
|
+
|
|
187
|
+
for (const entry of entries) {
|
|
188
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
189
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
190
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
191
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return NextResponse.json({ ok: true }, { status: 200 });
|
|
195
|
+
} catch (err) {
|
|
196
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
197
|
+
return NextResponse.json({ ok: false }, { status: 500 });
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Pages Router (`pages/api/logs.ts`):**
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
import type { NextApiRequest, NextApiResponse } from 'next';
|
|
206
|
+
import { logger } from '@/lib/logger';
|
|
207
|
+
|
|
208
|
+
export default async function handler(
|
|
209
|
+
req: NextApiRequest,
|
|
210
|
+
res: NextApiResponse
|
|
211
|
+
) {
|
|
212
|
+
if (req.method !== 'POST') {
|
|
213
|
+
return res.status(405).json({ ok: false });
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const body = typeof req.body === 'string' ? JSON.parse(req.body) : req.body;
|
|
218
|
+
const entries = Array.isArray(body) ? body : [body];
|
|
219
|
+
|
|
220
|
+
for (const entry of entries) {
|
|
221
|
+
const { level, messages, bindings, ...rest } = entry;
|
|
222
|
+
const msg = messages?.[0] ?? JSON.stringify(rest);
|
|
223
|
+
const logFn = level?.value >= 50 ? logger.error : logger.info;
|
|
224
|
+
logFn({ ...bindings, ...rest }, msg);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return res.status(200).json({ ok: true });
|
|
228
|
+
} catch (err) {
|
|
229
|
+
logger.error({ err }, 'Failed to ingest browser logs');
|
|
230
|
+
return res.status(500).json({ ok: false });
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### 5. Browser-Side: pino-browser with pino-transmit-http
|
|
236
|
+
|
|
237
|
+
Create `src/lib/browser-logger.ts` (or `lib/browser-logger.ts`):
|
|
238
|
+
|
|
239
|
+
```typescript
|
|
240
|
+
import pino from 'pino';
|
|
241
|
+
import pinoTransmitHttp from 'pino-transmit-http';
|
|
242
|
+
|
|
243
|
+
const isProd = process.env.NODE_ENV === 'production';
|
|
244
|
+
const logLevel =
|
|
245
|
+
process.env.NEXT_PUBLIC_LOG_LEVEL ?? (isProd ? 'error' : 'debug');
|
|
246
|
+
|
|
247
|
+
export const browserLogger = pino({
|
|
248
|
+
level: logLevel,
|
|
249
|
+
browser: {
|
|
250
|
+
transmit: pinoTransmitHttp({
|
|
251
|
+
url: '/api/logs',
|
|
252
|
+
throttle: 500,
|
|
253
|
+
useSendBeacon: true
|
|
254
|
+
})
|
|
255
|
+
}
|
|
256
|
+
});
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### 6. Wire Up Global Error Handlers (Browser)
|
|
260
|
+
|
|
261
|
+
Create `src/lib/init-browser-logging.ts` and import it from your root layout or `_app`:
|
|
262
|
+
|
|
263
|
+
```typescript
|
|
264
|
+
import { browserLogger } from './browser-logger';
|
|
265
|
+
|
|
266
|
+
export function initBrowserLogging() {
|
|
267
|
+
if (typeof window === 'undefined') return;
|
|
268
|
+
|
|
269
|
+
// Capture uncaught exceptions
|
|
270
|
+
window.onerror = (message, source, lineno, colno, error) => {
|
|
271
|
+
browserLogger.error(
|
|
272
|
+
{
|
|
273
|
+
err: error,
|
|
274
|
+
source,
|
|
275
|
+
lineno,
|
|
276
|
+
colno,
|
|
277
|
+
type: 'window.onerror'
|
|
278
|
+
},
|
|
279
|
+
String(message)
|
|
280
|
+
);
|
|
281
|
+
return false; // allow default handler to run
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
// Capture unhandled promise rejections
|
|
285
|
+
window.addEventListener('unhandledrejection', (event) => {
|
|
286
|
+
browserLogger.error(
|
|
287
|
+
{
|
|
288
|
+
reason: event.reason,
|
|
289
|
+
type: 'unhandledrejection'
|
|
290
|
+
},
|
|
291
|
+
'Unhandled promise rejection'
|
|
292
|
+
);
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
**Next.js App Router** – in `src/app/layout.tsx`:
|
|
298
|
+
|
|
299
|
+
```tsx
|
|
300
|
+
'use client';
|
|
301
|
+
|
|
302
|
+
import { useEffect } from 'react';
|
|
303
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
304
|
+
|
|
305
|
+
export default function RootLayout({
|
|
306
|
+
children
|
|
307
|
+
}: {
|
|
308
|
+
children: React.ReactNode;
|
|
309
|
+
}) {
|
|
310
|
+
useEffect(() => {
|
|
311
|
+
initBrowserLogging();
|
|
312
|
+
}, []);
|
|
313
|
+
|
|
314
|
+
return (
|
|
315
|
+
<html lang="en">
|
|
316
|
+
<body>{children}</body>
|
|
317
|
+
</html>
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
**Next.js Pages Router** – in `pages/_app.tsx`:
|
|
323
|
+
|
|
324
|
+
```tsx
|
|
325
|
+
import { useEffect } from 'react';
|
|
326
|
+
import { initBrowserLogging } from '@/lib/init-browser-logging';
|
|
327
|
+
|
|
328
|
+
export default function App({ Component, pageProps }) {
|
|
329
|
+
useEffect(() => {
|
|
330
|
+
initBrowserLogging();
|
|
331
|
+
}, []);
|
|
332
|
+
|
|
333
|
+
return <Component {...pageProps} />;
|
|
334
|
+
}
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### 7. Use the Browser Logger
|
|
338
|
+
|
|
339
|
+
Replace `console.log` with the browser logger in client components:
|
|
340
|
+
|
|
341
|
+
```typescript
|
|
342
|
+
import { browserLogger } from '@/lib/browser-logger';
|
|
343
|
+
|
|
344
|
+
// Instead of console.log('User clicked', data):
|
|
345
|
+
browserLogger.debug({ data }, 'User clicked');
|
|
346
|
+
|
|
347
|
+
// Instead of console.error(err):
|
|
348
|
+
browserLogger.error({ err }, 'Something failed');
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### 8. Environment Variables
|
|
352
|
+
|
|
353
|
+
Add to `.env.example`:
|
|
354
|
+
|
|
355
|
+
```env
|
|
356
|
+
# Log level: trace | debug | info | warn | error | fatal
|
|
357
|
+
# Development defaults to debug, production to error
|
|
358
|
+
LOG_LEVEL=debug
|
|
359
|
+
NEXT_PUBLIC_LOG_LEVEL=debug
|
|
360
|
+
|
|
361
|
+
# Fluentd (server-side)
|
|
362
|
+
FLUENT_HOST=127.0.0.1
|
|
363
|
+
FLUENT_PORT=24224
|
|
364
|
+
FLUENT_TAG=pino
|
|
365
|
+
FLUENT_ENABLED=false
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
For production, set `FLUENT_ENABLED=true` and configure `FLUENT_HOST` / `FLUENT_PORT` to point to your Fluentd instance.
|
|
369
|
+
|
|
370
|
+
### 9. Fluentd Configuration (Reference)
|
|
371
|
+
|
|
372
|
+
Minimal Fluentd config to receive logs on port 24224:
|
|
373
|
+
|
|
374
|
+
```xml
|
|
375
|
+
<source>
|
|
376
|
+
@type forward
|
|
377
|
+
port 24224
|
|
378
|
+
bind 0.0.0.0
|
|
379
|
+
</source>
|
|
380
|
+
|
|
381
|
+
<match pino.**>
|
|
382
|
+
@type stdout
|
|
383
|
+
</match>
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
Replace `@type stdout` with `@type elasticsearch`, `@type s3`, or another output as needed.
|
|
387
|
+
|
|
388
|
+
## Implementation Order
|
|
389
|
+
|
|
390
|
+
**For CLI projects:**
|
|
391
|
+
|
|
392
|
+
1. Install pino and pino-pretty (pino-pretty as devDependency)
|
|
393
|
+
2. Create `lib/logger.ts` with pretty output for TTY and JSON for CI
|
|
394
|
+
3. Replace `console.log` / `console.error` with `logger.info` / `logger.error`
|
|
395
|
+
4. Add `LOG_LEVEL` to `.env.example` if using env files
|
|
396
|
+
|
|
397
|
+
**For server/browser projects:**
|
|
398
|
+
|
|
399
|
+
1. Install dependencies (pino, pino-fluentd, pino-transmit-http, fluent-logger)
|
|
400
|
+
2. Create server-side logger (`lib/logger.ts`) with level from NODE_ENV
|
|
401
|
+
3. Create `/api/logs` route in Next.js
|
|
402
|
+
4. Create browser logger with pino-transmit-http pointing to `/api/logs`
|
|
403
|
+
5. Create `initBrowserLogging` and wire `window.onerror` and `unhandledrejection`
|
|
404
|
+
6. Import `initBrowserLogging` in root layout or `_app`
|
|
405
|
+
7. Add env vars to `.env.example`
|
|
406
|
+
8. Document Fluentd setup if deploying
|
|
407
|
+
|
|
408
|
+
## Log Level Summary
|
|
409
|
+
|
|
410
|
+
| Environment | Default Level |
|
|
411
|
+
| --------------------------------------- | ------------- |
|
|
412
|
+
| Development (NODE_ENV !== 'production') | DEBUG |
|
|
413
|
+
| Production | ERROR |
|
|
414
|
+
|
|
415
|
+
Override with `LOG_LEVEL` (server) or `NEXT_PUBLIC_LOG_LEVEL` (browser).
|
|
416
|
+
|
|
417
|
+
## Important Notes
|
|
418
|
+
|
|
419
|
+
- **CLI projects**: Use Pino with pino-pretty for human-readable output when running interactively (TTY). In CI (`CI=true`), output stays as JSON for parsing. Omit browser and Fluentd deps unless you need them.
|
|
420
|
+
- Use the **pipe approach** (`node app | pino-fluentd`) when possible; it keeps the app simple and lets pino-fluentd handle Fluentd connection.
|
|
421
|
+
- For Next.js in serverless (Vercel, etc.), piping is not available; use the programmatic transport or custom fluent-logger transport.
|
|
422
|
+
- The `/api/logs` endpoint receives batched JSON arrays from pino-transmit-http; parse and forward to your server logger.
|
|
423
|
+
- `pino-transmit-http` uses `navigator.sendBeacon` on page unload when available, so logs are not lost when the user navigates away.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Centralized Logging Rules
|
|
2
|
+
|
|
3
|
+
These rules are intended for Codex (CLI and app).
|
|
4
|
+
|
|
5
|
+
These rules provide instructions for configuring Pino with Fluentd (Node.js, Next.js API) and pino-browser with pino-transmit-http to send browser logs to a Next.js /api/logs endpoint.
|
|
6
|
+
|
|
7
|
+
---
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 'Centralized logging specialist - configures Pino with Fluentd for Node/Next.js, and pino-browser to /api/logs'
|
|
3
|
+
alwaysApply: false
|
|
4
|
+
globs:
|
|
5
|
+
- '**/lib/logger*.ts'
|
|
6
|
+
- '**/lib/browser-logger*.ts'
|
|
7
|
+
- '**/api/logs/**'
|
|
8
|
+
- '**/api/logs.*'
|
|
9
|
+
- 'package.json'
|
|
10
|
+
- '.env*'
|
|
11
|
+
---
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Configures Pino with Fluentd for Node/Next.js and pino-browser to /api/logs
|
|
3
|
+
mode: subagent
|
|
4
|
+
model: anthropic/claude-sonnet-4-20250514
|
|
5
|
+
temperature: 0.2
|
|
6
|
+
tools:
|
|
7
|
+
write: true
|
|
8
|
+
edit: true
|
|
9
|
+
bash: true
|
|
10
|
+
read: true
|
|
11
|
+
glob: true
|
|
12
|
+
grep: true
|
|
13
|
+
permission:
|
|
14
|
+
bash:
|
|
15
|
+
'git *': ask
|
|
16
|
+
'npm *': allow
|
|
17
|
+
'npx *': allow
|
|
18
|
+
'pnpm *': allow
|
|
19
|
+
'yarn *': allow
|
|
20
|
+
'cat *': allow
|
|
21
|
+
'*': ask
|
|
22
|
+
---
|
package/dist/agents.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
declare const PACKAGE_ROOT: string;
|
|
2
|
-
export declare const AGENT_IDS: readonly ["linting", "local-dev", "cicd", "observability"];
|
|
2
|
+
export declare const AGENT_IDS: readonly ["linting", "local-dev", "cicd", "observability", "logging"];
|
|
3
3
|
export type AgentId = (typeof AGENT_IDS)[number];
|
|
4
4
|
/**
|
|
5
5
|
* Resolve path to an agent directory
|
package/dist/agents.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,YAAY,QAA6B,CAAC;AAEhD,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAEA,QAAA,MAAM,YAAY,QAA6B,CAAC;AAEhD,eAAO,MAAM,SAAS,uEAMZ,CAAC;AACX,MAAM,MAAM,OAAO,GAAG,CAAC,OAAO,SAAS,CAAC,CAAC,MAAM,CAAC,CAAC;AAEjD;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;GAEG;AACH,wBAAgB,UAAU,IAAI,MAAM,EAAE,CAErC;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAErD;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,GAAG,MAAM,EAAE,CAQjE;AAED,OAAO,EAAE,YAAY,EAAE,CAAC"}
|
package/dist/agents.js
CHANGED
package/dist/agents.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":";;;;;;AAgBA,kCAEC;AAKD,gCAEC;AAKD,oCAEC;AAKD,sCAQC;AA7CD,gDAAwB;AAExB,MAAM,YAAY,GAAG,cAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AA6CvC,oCAAY;AA3CR,QAAA,SAAS,GAAG;IACvB,SAAS;IACT,WAAW;IACX,MAAM;IACN,eAAe;IACf,SAAS;CACD,CAAC;AAGX;;GAEG;AACH,SAAgB,WAAW,CAAC,OAAe;IACzC,OAAO,cAAI,CAAC,IAAI,CAAC,YAAY,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,SAAgB,UAAU;IACxB,OAAO,iBAAS,CAAC,KAAK,EAAE,CAAC;AAC3B,CAAC;AAED;;GAEG;AACH,SAAgB,YAAY,CAAC,OAAe;IAC1C,OAAQ,iBAA+B,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AAC5D,CAAC;AAED;;GAEG;AACH,SAAgB,aAAa,CAAC,MAAyB;IACrD,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAC1B,MAAM,MAAM,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;QAC/C,IAAI,MAAM;YAAE,OAAO,iBAAS,CAAC,KAAK,EAAE,CAAC;QACrC,OAAO,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,MAAM,KAAK,KAAK;QAAE,OAAO,iBAAS,CAAC,KAAK,EAAE,CAAC;IAC/C,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9C,CAAC"}
|
package/dist/build.d.ts
CHANGED
|
@@ -1,35 +1,51 @@
|
|
|
1
1
|
import type { Target } from './config';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* List rule suffixes for an agent. content.md → suffix ''; content-<suffix>.md → suffix.
|
|
4
|
+
* At least one of content.md or content-*.md must exist.
|
|
4
5
|
*/
|
|
5
|
-
export declare function
|
|
6
|
+
export declare function listRuleSuffixes(agentId: string): string[];
|
|
6
7
|
/**
|
|
7
|
-
* Read agent
|
|
8
|
+
* Read agent content for a rule. ruleSuffix '' or undefined = content.md; else content-<suffix>.md.
|
|
8
9
|
*/
|
|
9
|
-
export declare function
|
|
10
|
+
export declare function getContent(agentId: string, ruleSuffix?: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Read agent template file. Tries rule-specific template first (e.g. cursor-frontmatter-mcp.yaml).
|
|
13
|
+
*/
|
|
14
|
+
export declare function getTemplate(agentId: string, filename: string, ruleSuffix?: string): string;
|
|
10
15
|
/**
|
|
11
16
|
* Build content for Cursor (.mdc = frontmatter + content)
|
|
12
17
|
*/
|
|
13
|
-
export declare function buildCursorFormat(agentId: string): string;
|
|
18
|
+
export declare function buildCursorFormat(agentId: string, ruleSuffix?: string): string;
|
|
14
19
|
/**
|
|
15
20
|
* Build content for Claude (header + content)
|
|
16
21
|
*/
|
|
17
|
-
export declare function buildClaudeFormat(agentId: string): string;
|
|
22
|
+
export declare function buildClaudeFormat(agentId: string, ruleSuffix?: string): string;
|
|
18
23
|
/**
|
|
19
24
|
* Build content for OpenCode (YAML frontmatter + content)
|
|
20
25
|
*/
|
|
21
|
-
export declare function buildOpenCodeFormat(agentId: string): string;
|
|
26
|
+
export declare function buildOpenCodeFormat(agentId: string, ruleSuffix?: string): string;
|
|
27
|
+
/**
|
|
28
|
+
* Build content for Codex (header + content)
|
|
29
|
+
*/
|
|
30
|
+
export declare function buildCodexFormat(agentId: string, ruleSuffix?: string): string;
|
|
31
|
+
export declare function extractDescriptionFromFrontmatter(frontmatter: string): string | null;
|
|
32
|
+
export declare function getCodexRuleDescription(agentId: string, ruleSuffix?: string): string | null;
|
|
33
|
+
export declare function buildCodexAgentsMd(agents: string[]): string;
|
|
22
34
|
/**
|
|
23
|
-
* Build content for the given agent and
|
|
35
|
+
* Build content for the given agent, target, and optional rule suffix
|
|
24
36
|
*/
|
|
25
|
-
export declare function buildContent(agentId: string, target: Target): string;
|
|
37
|
+
export declare function buildContent(agentId: string, target: Target, ruleSuffix?: string): string;
|
|
26
38
|
/**
|
|
27
|
-
* Get destination path for installed agent file
|
|
39
|
+
* Get destination path for installed agent file. ruleSuffix '' or undefined = main rule; else <agentId>-<suffix>.
|
|
28
40
|
*/
|
|
29
|
-
export declare function getDestination(agentId: string, target: Target, projectRoot: string): {
|
|
41
|
+
export declare function getDestination(agentId: string, target: Target, projectRoot: string, ruleSuffix?: string): {
|
|
30
42
|
dir: string;
|
|
31
43
|
file: string;
|
|
32
44
|
};
|
|
45
|
+
/**
|
|
46
|
+
* Get destination for Codex AGENTS.md
|
|
47
|
+
*/
|
|
48
|
+
export declare function getCodexAgentsMdPath(projectRoot: string): string;
|
|
33
49
|
/**
|
|
34
50
|
* List supported targets
|
|
35
51
|
*/
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAQvC;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAyB1D;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAUvE;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAeR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAQR;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAIR;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAQR;AAuBD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAI7E;AAED,wBAAgB,iCAAiC,CAC/C,WAAW,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAef;AAED,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,GAAG,IAAI,CAWf;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAyB3D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,MAAM,GAClB,MAAM,CAaR;AAED;;GAEG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EACnB,UAAU,CAAC,EAAE,MAAM,GAClB;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA2B/B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEhE;AAED;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,EAAE,CAEtC"}
|