@r_masseater/cc-plugin-lib 0.0.1
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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/logger.d.ts +33 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +125 -0
- package/dist/wrapRun.d.ts +21 -0
- package/dist/wrapRun.d.ts.map +1 -0
- package/dist/wrapRun.js +28 -0
- package/package.json +36 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC"}
|
package/dist/index.js
ADDED
package/dist/logger.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Code プラグイン用のロガー
|
|
3
|
+
*
|
|
4
|
+
* 並行実行に対応したファイルベースのロギングを提供する。
|
|
5
|
+
* ログは XDG_STATE_HOME/masseater-plugins/{hookName}/ に日別で保存される。
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* using logger = HookLogger.fromFile(import.meta.filename);
|
|
10
|
+
* logger.debug("Hook started");
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
export declare class HookLogger {
|
|
14
|
+
private readonly logDir;
|
|
15
|
+
private readonly logger;
|
|
16
|
+
/**
|
|
17
|
+
* ファイルパスからHookLoggerを作成する
|
|
18
|
+
*
|
|
19
|
+
* @param filePath - import.meta.filename を渡す
|
|
20
|
+
*/
|
|
21
|
+
static fromFile(filePath: string): HookLogger;
|
|
22
|
+
private constructor();
|
|
23
|
+
[Symbol.dispose](): void;
|
|
24
|
+
private getLogFilePath;
|
|
25
|
+
private ensureLogDir;
|
|
26
|
+
private cleanupOldLogs;
|
|
27
|
+
debug(message: string, data?: Record<string, unknown>): void;
|
|
28
|
+
info(message: string, data?: Record<string, unknown>): void;
|
|
29
|
+
warn(message: string, data?: Record<string, unknown>): void;
|
|
30
|
+
error(message: string, data?: Record<string, unknown>): void;
|
|
31
|
+
fatal(message: string, error: unknown): void;
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=logger.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../src/logger.ts"],"names":[],"mappings":"AAeA;;;;;;;;;;;GAWG;AACH,qBAAa,UAAU;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAc;IAErC;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,UAAU;IAM7C,OAAO;IA+BP,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI;IAIxB,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,YAAY;IAMpB,OAAO,CAAC,cAAc;IAiBtB,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQ5D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQ3D,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQ3D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAQ5D,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;CAO5C"}
|
package/dist/logger.js
ADDED
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, unlinkSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { basename, join } from "node:path";
|
|
4
|
+
import pino from "pino";
|
|
5
|
+
const KEEP_COUNT = Number(process.env.MASSEATER_PLUGINS_KEEP_COUNT) || 30;
|
|
6
|
+
function getHomeDir() {
|
|
7
|
+
const home = process.env.HOME ?? homedir();
|
|
8
|
+
if (!home) {
|
|
9
|
+
throw new Error("Could not determine home directory");
|
|
10
|
+
}
|
|
11
|
+
return home;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Claude Code プラグイン用のロガー
|
|
15
|
+
*
|
|
16
|
+
* 並行実行に対応したファイルベースのロギングを提供する。
|
|
17
|
+
* ログは XDG_STATE_HOME/masseater-plugins/{hookName}/ に日別で保存される。
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* using logger = HookLogger.fromFile(import.meta.filename);
|
|
22
|
+
* logger.debug("Hook started");
|
|
23
|
+
* ```
|
|
24
|
+
*/
|
|
25
|
+
export class HookLogger {
|
|
26
|
+
logDir;
|
|
27
|
+
logger;
|
|
28
|
+
/**
|
|
29
|
+
* ファイルパスからHookLoggerを作成する
|
|
30
|
+
*
|
|
31
|
+
* @param filePath - import.meta.filename を渡す
|
|
32
|
+
*/
|
|
33
|
+
static fromFile(filePath) {
|
|
34
|
+
const fileName = basename(filePath);
|
|
35
|
+
const hookName = fileName.replace(/\.[^.]+$/, "");
|
|
36
|
+
return new HookLogger(hookName);
|
|
37
|
+
}
|
|
38
|
+
constructor(hookName) {
|
|
39
|
+
const home = getHomeDir();
|
|
40
|
+
const xdgStateHome = process.env.XDG_STATE_HOME ?? `${home}/.local/state`;
|
|
41
|
+
this.logDir = `${xdgStateHome}/masseater-plugins/${hookName}`;
|
|
42
|
+
this.ensureLogDir();
|
|
43
|
+
const logPath = this.getLogFilePath();
|
|
44
|
+
// pino with file transport
|
|
45
|
+
// sync: true + append: true for concurrent writes from multiple instances
|
|
46
|
+
// Each JSON line is atomic write (< PIPE_BUF)
|
|
47
|
+
this.logger = pino({
|
|
48
|
+
level: process.env.MASSEATER_PLUGINS_LOG_LEVEL ?? "debug",
|
|
49
|
+
timestamp: pino.stdTimeFunctions.isoTime,
|
|
50
|
+
base: { pid: process.pid, hook: hookName },
|
|
51
|
+
formatters: {
|
|
52
|
+
level: (label) => ({ level: label }),
|
|
53
|
+
},
|
|
54
|
+
}, pino.destination({
|
|
55
|
+
dest: logPath,
|
|
56
|
+
sync: true,
|
|
57
|
+
append: true,
|
|
58
|
+
mkdir: true,
|
|
59
|
+
}));
|
|
60
|
+
this.debug("Logger initialized");
|
|
61
|
+
}
|
|
62
|
+
[Symbol.dispose]() {
|
|
63
|
+
this.cleanupOldLogs();
|
|
64
|
+
}
|
|
65
|
+
getLogFilePath() {
|
|
66
|
+
const date = new Date().toISOString().split("T")[0];
|
|
67
|
+
return join(this.logDir, `${date}.log`);
|
|
68
|
+
}
|
|
69
|
+
ensureLogDir() {
|
|
70
|
+
if (!existsSync(this.logDir)) {
|
|
71
|
+
mkdirSync(this.logDir, { recursive: true });
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
cleanupOldLogs() {
|
|
75
|
+
if (!existsSync(this.logDir)) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
const files = readdirSync(this.logDir)
|
|
79
|
+
.filter((f) => /^\d{4}-\d{2}-\d{2}\.log$/.test(f))
|
|
80
|
+
.sort()
|
|
81
|
+
.reverse();
|
|
82
|
+
const filesToDelete = files.slice(KEEP_COUNT);
|
|
83
|
+
for (const file of filesToDelete) {
|
|
84
|
+
unlinkSync(join(this.logDir, file));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
debug(message, data) {
|
|
88
|
+
if (data) {
|
|
89
|
+
this.logger.debug(data, message);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
this.logger.debug(message);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
info(message, data) {
|
|
96
|
+
if (data) {
|
|
97
|
+
this.logger.info(data, message);
|
|
98
|
+
}
|
|
99
|
+
else {
|
|
100
|
+
this.logger.info(message);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
warn(message, data) {
|
|
104
|
+
if (data) {
|
|
105
|
+
this.logger.warn(data, message);
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
this.logger.warn(message);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
error(message, data) {
|
|
112
|
+
if (data) {
|
|
113
|
+
this.logger.error(data, message);
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
this.logger.error(message);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
fatal(message, error) {
|
|
120
|
+
const errorInfo = error instanceof Error
|
|
121
|
+
? { message: error.message, stack: error.stack, name: error.name }
|
|
122
|
+
: { message: String(error) };
|
|
123
|
+
this.logger.fatal({ error: errorInfo }, message);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { HookLogger } from "./logger.js";
|
|
2
|
+
type Awaitable<T> = T | Promise<T>;
|
|
3
|
+
/**
|
|
4
|
+
* hook の run 関数をラップしてロギングとエラーハンドリングを追加する
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* const hook = defineHook({
|
|
9
|
+
* trigger: { Stop: true },
|
|
10
|
+
* run: wrapRun(logger, (context) => {
|
|
11
|
+
* // hook logic
|
|
12
|
+
* return context.success({});
|
|
13
|
+
* }),
|
|
14
|
+
* });
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export declare function wrapRun<TContext extends {
|
|
18
|
+
input: unknown;
|
|
19
|
+
}, TResult>(logger: HookLogger, run: (context: TContext) => Awaitable<TResult>): (context: TContext) => Promise<TResult>;
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=wrapRun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wrapRun.d.ts","sourceRoot":"","sources":["../src/wrapRun.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAE9C,KAAK,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;AAEnC;;;;;;;;;;;;;GAaG;AACH,wBAAgB,OAAO,CAAC,QAAQ,SAAS;IAAE,KAAK,EAAE,OAAO,CAAA;CAAE,EAAE,OAAO,EACnE,MAAM,EAAE,UAAU,EAClB,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,KAAK,SAAS,CAAC,OAAO,CAAC,GAC5C,CAAC,OAAO,EAAE,QAAQ,KAAK,OAAO,CAAC,OAAO,CAAC,CAYzC"}
|
package/dist/wrapRun.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hook の run 関数をラップしてロギングとエラーハンドリングを追加する
|
|
3
|
+
*
|
|
4
|
+
* @example
|
|
5
|
+
* ```typescript
|
|
6
|
+
* const hook = defineHook({
|
|
7
|
+
* trigger: { Stop: true },
|
|
8
|
+
* run: wrapRun(logger, (context) => {
|
|
9
|
+
* // hook logic
|
|
10
|
+
* return context.success({});
|
|
11
|
+
* }),
|
|
12
|
+
* });
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export function wrapRun(logger, run) {
|
|
16
|
+
return async (context) => {
|
|
17
|
+
logger.debug("Hook triggered", { input: context.input });
|
|
18
|
+
try {
|
|
19
|
+
const result = await run(context);
|
|
20
|
+
logger.debug("Hook completed");
|
|
21
|
+
return result;
|
|
22
|
+
}
|
|
23
|
+
catch (error) {
|
|
24
|
+
logger.fatal("Hook failed", error);
|
|
25
|
+
throw error;
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@r_masseater/cc-plugin-lib",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Common library for Claude Code plugins - logging and error handling utilities",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js"
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
"files": ["dist"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"check": "biome check",
|
|
18
|
+
"check:fix": "biome check --write",
|
|
19
|
+
"prepublishOnly": "bun run build"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"pino": "^9.6.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@biomejs/biome": "^2.3.8",
|
|
26
|
+
"@types/bun": "^1.3.4",
|
|
27
|
+
"typescript": "^5.9.3"
|
|
28
|
+
},
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/masseater/claude-code-plugin"
|
|
32
|
+
},
|
|
33
|
+
"keywords": ["claude-code", "plugin", "hooks", "logging"],
|
|
34
|
+
"author": "masseater",
|
|
35
|
+
"license": "MIT"
|
|
36
|
+
}
|