@giwonn/claude-daily-review 0.2.3 → 0.3.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/.claude-plugin/marketplace.json +29 -0
- package/.github/workflows/publish.yml +24 -0
- package/.github/workflows/update-marketplace.yml +28 -0
- package/docs/superpowers/plans/2026-03-28-claude-daily-review-plan.md +2130 -0
- package/docs/superpowers/plans/2026-03-28-no-build-refactor-plan.md +1158 -0
- package/docs/superpowers/plans/2026-03-28-storage-adapter-refactor-plan.md +2207 -0
- package/docs/superpowers/specs/2026-03-28-claude-daily-review-design.md +582 -0
- package/docs/superpowers/specs/2026-03-28-no-build-refactor-design.md +333 -0
- package/docs/superpowers/specs/2026-03-28-storage-adapter-refactor-design.md +365 -0
- package/hooks/hooks.json +3 -2
- package/hooks/on-stop.mjs +24 -0
- package/hooks/run-hook.cmd +27 -0
- package/hooks/session-start-check +27 -0
- package/lib/config.mjs +122 -0
- package/lib/github-auth.mjs +44 -0
- package/lib/github-storage.mjs +81 -0
- package/lib/merge.mjs +51 -0
- package/lib/periods.mjs +82 -0
- package/lib/raw-logger.mjs +19 -0
- package/lib/storage-cli.mjs +48 -0
- package/lib/storage.mjs +63 -0
- package/lib/types.d.ts +64 -0
- package/lib/vault.mjs +43 -0
- package/package.json +3 -23
- package/prompts/session-end.md +5 -5
- package/prompts/session-start.md +5 -5
- package/dist/on-session-start-check.js +0 -56
- package/dist/on-session-start-check.js.map +0 -1
- package/dist/on-stop.js +0 -274
- package/dist/on-stop.js.map +0 -1
- package/dist/storage-cli.js +0 -267
- package/dist/storage-cli.js.map +0 -1
package/lib/vault.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// @ts-check
|
|
2
|
+
/** @typedef {import('./types.d.ts').StorageAdapter} StorageAdapter */
|
|
3
|
+
/** @typedef {import('./types.d.ts').Periods} Periods */
|
|
4
|
+
|
|
5
|
+
/** @param {string} sessionId @returns {string} */
|
|
6
|
+
export function getRawDir(sessionId) { return `.raw/${sessionId}`; }
|
|
7
|
+
|
|
8
|
+
/** @returns {string} */
|
|
9
|
+
export function getReviewsDir() { return '.reviews'; }
|
|
10
|
+
|
|
11
|
+
/** @param {string} date @returns {string} */
|
|
12
|
+
export function getDailyPath(date) { return `daily/${date}.md`; }
|
|
13
|
+
|
|
14
|
+
/** @param {string} week @returns {string} */
|
|
15
|
+
export function getWeeklyPath(week) { return `weekly/${week}.md`; }
|
|
16
|
+
|
|
17
|
+
/** @param {string} month @returns {string} */
|
|
18
|
+
export function getMonthlyPath(month) { return `monthly/${month}.md`; }
|
|
19
|
+
|
|
20
|
+
/** @param {string} quarter @returns {string} */
|
|
21
|
+
export function getQuarterlyPath(quarter) { return `quarterly/${quarter}.md`; }
|
|
22
|
+
|
|
23
|
+
/** @param {string} year @returns {string} */
|
|
24
|
+
export function getYearlyPath(year) { return `yearly/${year}.md`; }
|
|
25
|
+
|
|
26
|
+
/** @param {string} projectName @param {string} date @returns {string} */
|
|
27
|
+
export function getProjectDailyPath(projectName, date) { return `projects/${projectName}/${date}.md`; }
|
|
28
|
+
|
|
29
|
+
/** @param {string} projectName @returns {string} */
|
|
30
|
+
export function getProjectSummaryPath(projectName) { return `projects/${projectName}/summary.md`; }
|
|
31
|
+
|
|
32
|
+
/** @param {string} date @returns {string} */
|
|
33
|
+
export function getUncategorizedPath(date) { return `uncategorized/${date}.md`; }
|
|
34
|
+
|
|
35
|
+
/** @param {StorageAdapter} storage @param {Periods} periods @returns {Promise<void>} */
|
|
36
|
+
export async function ensureVaultDirectories(storage, periods) {
|
|
37
|
+
const dirs = ['daily', 'projects', 'uncategorized', '.raw', '.reviews'];
|
|
38
|
+
if (periods.weekly) dirs.push('weekly');
|
|
39
|
+
if (periods.monthly) dirs.push('monthly');
|
|
40
|
+
if (periods.quarterly) dirs.push('quarterly');
|
|
41
|
+
if (periods.yearly) dirs.push('yearly');
|
|
42
|
+
for (const dir of dirs) { await storage.mkdir(dir); }
|
|
43
|
+
}
|
package/package.json
CHANGED
|
@@ -1,31 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@giwonn/claude-daily-review",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Claude Code plugin that auto-captures conversations for daily review and career documentation",
|
|
3
|
+
"version": "0.3.0",
|
|
5
4
|
"type": "module",
|
|
6
|
-
"
|
|
7
|
-
"scripts": {
|
|
8
|
-
"build": "tsup",
|
|
9
|
-
"test": "vitest run",
|
|
10
|
-
"test:watch": "vitest"
|
|
11
|
-
},
|
|
12
|
-
"keywords": ["claude-code", "plugin", "daily-review", "obsidian"],
|
|
5
|
+
"description": "Claude Code plugin that auto-captures conversations for daily review and career documentation",
|
|
13
6
|
"repository": {
|
|
14
7
|
"type": "git",
|
|
15
8
|
"url": "https://github.com/giwonn/claude-daily-review"
|
|
16
9
|
},
|
|
17
|
-
"license": "MIT"
|
|
18
|
-
"files": [
|
|
19
|
-
"dist",
|
|
20
|
-
"hooks",
|
|
21
|
-
"prompts",
|
|
22
|
-
"skills",
|
|
23
|
-
"README.md"
|
|
24
|
-
],
|
|
25
|
-
"devDependencies": {
|
|
26
|
-
"typescript": "^5.4.0",
|
|
27
|
-
"vitest": "^3.0.0",
|
|
28
|
-
"tsup": "^8.0.0",
|
|
29
|
-
"@types/node": "^20.0.0"
|
|
30
|
-
}
|
|
10
|
+
"license": "MIT"
|
|
31
11
|
}
|
package/prompts/session-end.md
CHANGED
|
@@ -9,11 +9,11 @@ This plugin supports two storage backends: **local** and **github**. After readi
|
|
|
9
9
|
- **If `storage.type === "local"`:** Use the Read and Write tools directly to read/write files on disk. Paths are relative to `storage.local.basePath`.
|
|
10
10
|
- **If `storage.type === "github"`:** Use the storage-cli tool via Bash for all file operations. The CLI commands are:
|
|
11
11
|
```bash
|
|
12
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
13
|
-
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/
|
|
14
|
-
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/
|
|
15
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
16
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
12
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" read <path>
|
|
13
|
+
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" write <path>
|
|
14
|
+
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" append <path>
|
|
15
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" list <dir>
|
|
16
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" exists <path>
|
|
17
17
|
```
|
|
18
18
|
All `<path>` arguments are relative to the configured `storage.github.basePath` (e.g., `daily-review`). The CLI handles GitHub API calls internally.
|
|
19
19
|
|
package/prompts/session-start.md
CHANGED
|
@@ -9,11 +9,11 @@ This plugin supports two storage backends: **local** and **github**. After readi
|
|
|
9
9
|
- **If `storage.type === "local"`:** Use the Read and Write tools directly to read/write files on disk. Paths are relative to `storage.local.basePath`.
|
|
10
10
|
- **If `storage.type === "github"`:** Use the storage-cli tool via Bash for all file operations. The CLI commands are:
|
|
11
11
|
```bash
|
|
12
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
13
|
-
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/
|
|
14
|
-
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/
|
|
15
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
16
|
-
node "${CLAUDE_PLUGIN_ROOT}/
|
|
12
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" read <path>
|
|
13
|
+
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" write <path>
|
|
14
|
+
echo "<content>" | node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" append <path>
|
|
15
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" list <dir>
|
|
16
|
+
node "${CLAUDE_PLUGIN_ROOT}/lib/storage-cli.mjs" exists <path>
|
|
17
17
|
```
|
|
18
18
|
All `<path>` arguments are relative to the configured `storage.github.basePath` (e.g., `daily-review`). The CLI handles GitHub API calls internally.
|
|
19
19
|
|
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// src/core/config.ts
|
|
2
|
-
import { readFileSync, writeFileSync, existsSync, mkdirSync } from "fs";
|
|
3
|
-
import { dirname, join } from "path";
|
|
4
|
-
function getConfigPath() {
|
|
5
|
-
const dataDir = process.env.CLAUDE_PLUGIN_DATA;
|
|
6
|
-
if (!dataDir) {
|
|
7
|
-
throw new Error("CLAUDE_PLUGIN_DATA environment variable is not set");
|
|
8
|
-
}
|
|
9
|
-
return join(dataDir, "config.json");
|
|
10
|
-
}
|
|
11
|
-
function isOldConfig(raw) {
|
|
12
|
-
if (!raw || typeof raw !== "object") return false;
|
|
13
|
-
return "vaultPath" in raw && "reviewFolder" in raw;
|
|
14
|
-
}
|
|
15
|
-
function migrateOldConfig(old) {
|
|
16
|
-
return {
|
|
17
|
-
storage: {
|
|
18
|
-
type: "local",
|
|
19
|
-
local: {
|
|
20
|
-
basePath: join(old.vaultPath, old.reviewFolder)
|
|
21
|
-
}
|
|
22
|
-
},
|
|
23
|
-
language: old.language,
|
|
24
|
-
periods: old.periods,
|
|
25
|
-
profile: old.profile
|
|
26
|
-
};
|
|
27
|
-
}
|
|
28
|
-
function loadConfig() {
|
|
29
|
-
const configPath = getConfigPath();
|
|
30
|
-
if (!existsSync(configPath)) return null;
|
|
31
|
-
const raw = JSON.parse(readFileSync(configPath, "utf-8"));
|
|
32
|
-
if (isOldConfig(raw)) {
|
|
33
|
-
const migrated = migrateOldConfig(raw);
|
|
34
|
-
saveConfig(migrated);
|
|
35
|
-
return migrated;
|
|
36
|
-
}
|
|
37
|
-
return raw;
|
|
38
|
-
}
|
|
39
|
-
function saveConfig(config) {
|
|
40
|
-
const configPath = getConfigPath();
|
|
41
|
-
mkdirSync(dirname(configPath), { recursive: true });
|
|
42
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// src/hooks/on-session-start-check.ts
|
|
46
|
-
try {
|
|
47
|
-
const config = loadConfig();
|
|
48
|
-
if (!config) {
|
|
49
|
-
process.stderr.write("daily-review: \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. /daily-review-setup \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.\n");
|
|
50
|
-
process.exit(2);
|
|
51
|
-
}
|
|
52
|
-
} catch {
|
|
53
|
-
process.stderr.write("daily-review: \uC124\uC815\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. /daily-review-setup \uC744 \uC2E4\uD589\uD574\uC8FC\uC138\uC694.\n");
|
|
54
|
-
process.exit(2);
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=on-session-start-check.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/config.ts","../src/hooks/on-session-start-check.ts"],"sourcesContent":["import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport type { StorageAdapter } from \"./storage.js\";\nimport { LocalStorageAdapter } from \"./local-storage.js\";\n\nexport interface Profile {\n company: string;\n role: string;\n team: string;\n context: string;\n}\n\nexport interface Periods {\n daily: true;\n weekly: boolean;\n monthly: boolean;\n quarterly: boolean;\n yearly: boolean;\n}\n\nexport interface LocalStorageConfig {\n basePath: string;\n}\n\nexport interface GitHubStorageConfig {\n owner: string;\n repo: string;\n token: string;\n basePath: string;\n}\n\nexport interface StorageConfig {\n type: \"local\" | \"github\";\n local?: LocalStorageConfig;\n github?: GitHubStorageConfig;\n}\n\nexport interface Config {\n storage: StorageConfig;\n language: string;\n periods: Periods;\n profile: Profile;\n}\n\ninterface OldConfig {\n vaultPath: string;\n reviewFolder: string;\n language: string;\n periods: Periods;\n profile: Profile;\n}\n\nconst DEFAULT_PERIODS: Periods = {\n daily: true,\n weekly: true,\n monthly: true,\n quarterly: true,\n yearly: false,\n};\n\nconst DEFAULT_PROFILE: Profile = {\n company: \"\",\n role: \"\",\n team: \"\",\n context: \"\",\n};\n\nexport function getConfigPath(): string {\n const dataDir = process.env.CLAUDE_PLUGIN_DATA;\n if (!dataDir) {\n throw new Error(\"CLAUDE_PLUGIN_DATA environment variable is not set\");\n }\n return join(dataDir, \"config.json\");\n}\n\nfunction isOldConfig(raw: unknown): raw is OldConfig {\n if (!raw || typeof raw !== \"object\") return false;\n return \"vaultPath\" in raw && \"reviewFolder\" in raw;\n}\n\nfunction migrateOldConfig(old: OldConfig): Config {\n return {\n storage: {\n type: \"local\",\n local: {\n basePath: join(old.vaultPath, old.reviewFolder),\n },\n },\n language: old.language,\n periods: old.periods,\n profile: old.profile,\n };\n}\n\nexport function loadConfig(): Config | null {\n const configPath = getConfigPath();\n if (!existsSync(configPath)) return null;\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (isOldConfig(raw)) {\n const migrated = migrateOldConfig(raw);\n saveConfig(migrated);\n return migrated;\n }\n return raw as Config;\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n mkdirSync(dirname(configPath), { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function validateConfig(config: unknown): config is Config {\n if (!config || typeof config !== \"object\") return false;\n const c = config as Record<string, unknown>;\n if (!c.storage || typeof c.storage !== \"object\") return false;\n const s = c.storage as Record<string, unknown>;\n if (s.type !== \"local\" && s.type !== \"github\") return false;\n if (s.type === \"local\") {\n if (!s.local || typeof s.local !== \"object\") return false;\n const l = s.local as Record<string, unknown>;\n if (typeof l.basePath !== \"string\" || l.basePath === \"\") return false;\n }\n if (s.type === \"github\") {\n if (!s.github || typeof s.github !== \"object\") return false;\n const g = s.github as Record<string, unknown>;\n if (typeof g.owner !== \"string\" || !g.owner) return false;\n if (typeof g.repo !== \"string\" || !g.repo) return false;\n if (typeof g.token !== \"string\" || !g.token) return false;\n }\n return true;\n}\n\nexport function createDefaultLocalConfig(basePath: string): Config {\n return {\n storage: { type: \"local\", local: { basePath } },\n language: \"ko\",\n periods: { ...DEFAULT_PERIODS },\n profile: { ...DEFAULT_PROFILE },\n };\n}\n\nexport function createDefaultGitHubConfig(owner: string, repo: string, token: string): Config {\n return {\n storage: { type: \"github\", github: { owner, repo, token, basePath: \"daily-review\" } },\n language: \"ko\",\n periods: { ...DEFAULT_PERIODS },\n profile: { ...DEFAULT_PROFILE },\n };\n}\n\nexport async function createStorageAdapter(config: Config): Promise<StorageAdapter> {\n if (config.storage.type === \"local\") {\n return new LocalStorageAdapter(config.storage.local!.basePath);\n }\n if (config.storage.type === \"github\") {\n const { GitHubStorageAdapter } = await import(\"./github-storage.js\");\n const g = config.storage.github!;\n return new GitHubStorageAdapter(g.owner, g.repo, g.token, g.basePath);\n }\n throw new Error(`Unknown storage type: ${(config.storage as any).type}`);\n}\n","import { loadConfig } from \"../core/config.js\";\n\ntry {\n const config = loadConfig();\n if (!config) {\n process.stderr.write(\"daily-review: 설정이 없습니다. /daily-review-setup 을 실행해주세요.\\n\");\n process.exit(2);\n }\n} catch {\n process.stderr.write(\"daily-review: 설정이 없습니다. /daily-review-setup 을 실행해주세요.\\n\");\n process.exit(2);\n}\n"],"mappings":";AAAA,SAAS,cAAc,eAAe,YAAY,iBAAiB;AACnE,SAAS,SAAS,YAAY;AAkEvB,SAAS,gBAAwB;AACtC,QAAM,UAAU,QAAQ,IAAI;AAC5B,MAAI,CAAC,SAAS;AACZ,UAAM,IAAI,MAAM,oDAAoD;AAAA,EACtE;AACA,SAAO,KAAK,SAAS,aAAa;AACpC;AAEA,SAAS,YAAY,KAAgC;AACnD,MAAI,CAAC,OAAO,OAAO,QAAQ,SAAU,QAAO;AAC5C,SAAO,eAAe,OAAO,kBAAkB;AACjD;AAEA,SAAS,iBAAiB,KAAwB;AAChD,SAAO;AAAA,IACL,SAAS;AAAA,MACP,MAAM;AAAA,MACN,OAAO;AAAA,QACL,UAAU,KAAK,IAAI,WAAW,IAAI,YAAY;AAAA,MAChD;AAAA,IACF;AAAA,IACA,UAAU,IAAI;AAAA,IACd,SAAS,IAAI;AAAA,IACb,SAAS,IAAI;AAAA,EACf;AACF;AAEO,SAAS,aAA4B;AAC1C,QAAM,aAAa,cAAc;AACjC,MAAI,CAAC,WAAW,UAAU,EAAG,QAAO;AACpC,QAAM,MAAM,KAAK,MAAM,aAAa,YAAY,OAAO,CAAC;AACxD,MAAI,YAAY,GAAG,GAAG;AACpB,UAAM,WAAW,iBAAiB,GAAG;AACrC,eAAW,QAAQ;AACnB,WAAO;AAAA,EACT;AACA,SAAO;AACT;AAEO,SAAS,WAAW,QAAsB;AAC/C,QAAM,aAAa,cAAc;AACjC,YAAU,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAClD,gBAAc,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,GAAG,OAAO;AACpE;;;AC5GA,IAAI;AACF,QAAM,SAAS,WAAW;AAC1B,MAAI,CAAC,QAAQ;AACX,YAAQ,OAAO,MAAM,+HAAyD;AAC9E,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF,QAAQ;AACN,UAAQ,OAAO,MAAM,+HAAyD;AAC9E,UAAQ,KAAK,CAAC;AAChB;","names":[]}
|
package/dist/on-stop.js
DELETED
|
@@ -1,274 +0,0 @@
|
|
|
1
|
-
var __defProp = Object.defineProperty;
|
|
2
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
3
|
-
var __esm = (fn, res) => function __init() {
|
|
4
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
5
|
-
};
|
|
6
|
-
var __export = (target, all) => {
|
|
7
|
-
for (var name in all)
|
|
8
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
9
|
-
};
|
|
10
|
-
|
|
11
|
-
// src/core/github-storage.ts
|
|
12
|
-
var github_storage_exports = {};
|
|
13
|
-
__export(github_storage_exports, {
|
|
14
|
-
GitHubStorageAdapter: () => GitHubStorageAdapter
|
|
15
|
-
});
|
|
16
|
-
var GitHubStorageAdapter;
|
|
17
|
-
var init_github_storage = __esm({
|
|
18
|
-
"src/core/github-storage.ts"() {
|
|
19
|
-
"use strict";
|
|
20
|
-
GitHubStorageAdapter = class {
|
|
21
|
-
constructor(owner, repo, token, basePath) {
|
|
22
|
-
this.owner = owner;
|
|
23
|
-
this.repo = repo;
|
|
24
|
-
this.token = token;
|
|
25
|
-
this.basePath = basePath;
|
|
26
|
-
this.baseUrl = `https://api.github.com/repos/${owner}/${repo}/contents`;
|
|
27
|
-
this.headers = {
|
|
28
|
-
Authorization: `Bearer ${token}`,
|
|
29
|
-
Accept: "application/vnd.github.v3+json",
|
|
30
|
-
"Content-Type": "application/json"
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
baseUrl;
|
|
34
|
-
headers;
|
|
35
|
-
getUrl(path) {
|
|
36
|
-
return `${this.baseUrl}/${this.basePath}/${path}`;
|
|
37
|
-
}
|
|
38
|
-
async getSha(path) {
|
|
39
|
-
const res = await fetch(this.getUrl(path), { method: "GET", headers: this.headers });
|
|
40
|
-
if (res.status === 404) return null;
|
|
41
|
-
const data = await res.json();
|
|
42
|
-
return data.sha || null;
|
|
43
|
-
}
|
|
44
|
-
async read(path) {
|
|
45
|
-
const res = await fetch(this.getUrl(path), { method: "GET", headers: this.headers });
|
|
46
|
-
if (res.status === 404) return null;
|
|
47
|
-
const data = await res.json();
|
|
48
|
-
const content = data.content;
|
|
49
|
-
return Buffer.from(content, "base64").toString("utf-8");
|
|
50
|
-
}
|
|
51
|
-
async write(path, content) {
|
|
52
|
-
const sha = await this.getSha(path);
|
|
53
|
-
const body = {
|
|
54
|
-
message: `update ${path}`,
|
|
55
|
-
content: Buffer.from(content).toString("base64")
|
|
56
|
-
};
|
|
57
|
-
if (sha) body.sha = sha;
|
|
58
|
-
const res = await fetch(this.getUrl(path), {
|
|
59
|
-
method: "PUT",
|
|
60
|
-
headers: this.headers,
|
|
61
|
-
body: JSON.stringify(body)
|
|
62
|
-
});
|
|
63
|
-
if (!res.ok && res.status === 409) {
|
|
64
|
-
const freshSha = await this.getSha(path);
|
|
65
|
-
if (freshSha) body.sha = freshSha;
|
|
66
|
-
await fetch(this.getUrl(path), {
|
|
67
|
-
method: "PUT",
|
|
68
|
-
headers: this.headers,
|
|
69
|
-
body: JSON.stringify(body)
|
|
70
|
-
});
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
async append(path, content) {
|
|
74
|
-
const existing = await this.read(path);
|
|
75
|
-
const newContent = existing ? existing + content : content;
|
|
76
|
-
await this.write(path, newContent);
|
|
77
|
-
}
|
|
78
|
-
async exists(path) {
|
|
79
|
-
const res = await fetch(this.getUrl(path), { method: "GET", headers: this.headers });
|
|
80
|
-
return res.status !== 404;
|
|
81
|
-
}
|
|
82
|
-
async list(dir) {
|
|
83
|
-
const res = await fetch(this.getUrl(dir), { method: "GET", headers: this.headers });
|
|
84
|
-
if (res.status === 404) return [];
|
|
85
|
-
const data = await res.json();
|
|
86
|
-
if (!Array.isArray(data)) return [];
|
|
87
|
-
return data.map((entry) => entry.name);
|
|
88
|
-
}
|
|
89
|
-
async mkdir(_dir) {
|
|
90
|
-
}
|
|
91
|
-
async isDirectory(path) {
|
|
92
|
-
const res = await fetch(this.getUrl(path), { method: "GET", headers: this.headers });
|
|
93
|
-
if (res.status === 404) return false;
|
|
94
|
-
const data = await res.json();
|
|
95
|
-
return Array.isArray(data);
|
|
96
|
-
}
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
// src/hooks/on-stop.ts
|
|
102
|
-
import { fileURLToPath } from "url";
|
|
103
|
-
import { resolve } from "path";
|
|
104
|
-
|
|
105
|
-
// src/core/config.ts
|
|
106
|
-
import { readFileSync as readFileSync2, writeFileSync as writeFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2 } from "fs";
|
|
107
|
-
import { dirname as dirname2, join as join2 } from "path";
|
|
108
|
-
|
|
109
|
-
// src/core/local-storage.ts
|
|
110
|
-
import {
|
|
111
|
-
readFileSync,
|
|
112
|
-
writeFileSync,
|
|
113
|
-
appendFileSync,
|
|
114
|
-
existsSync,
|
|
115
|
-
mkdirSync,
|
|
116
|
-
readdirSync,
|
|
117
|
-
statSync
|
|
118
|
-
} from "fs";
|
|
119
|
-
import { dirname, join } from "path";
|
|
120
|
-
var LocalStorageAdapter = class {
|
|
121
|
-
constructor(basePath) {
|
|
122
|
-
this.basePath = basePath;
|
|
123
|
-
}
|
|
124
|
-
resolve(path) {
|
|
125
|
-
return join(this.basePath, path);
|
|
126
|
-
}
|
|
127
|
-
async read(path) {
|
|
128
|
-
const full = this.resolve(path);
|
|
129
|
-
if (!existsSync(full)) return null;
|
|
130
|
-
return readFileSync(full, "utf-8");
|
|
131
|
-
}
|
|
132
|
-
async write(path, content) {
|
|
133
|
-
const full = this.resolve(path);
|
|
134
|
-
mkdirSync(dirname(full), { recursive: true });
|
|
135
|
-
writeFileSync(full, content, "utf-8");
|
|
136
|
-
}
|
|
137
|
-
async append(path, content) {
|
|
138
|
-
const full = this.resolve(path);
|
|
139
|
-
mkdirSync(dirname(full), { recursive: true });
|
|
140
|
-
appendFileSync(full, content, "utf-8");
|
|
141
|
-
}
|
|
142
|
-
async exists(path) {
|
|
143
|
-
return existsSync(this.resolve(path));
|
|
144
|
-
}
|
|
145
|
-
async list(dir) {
|
|
146
|
-
const full = this.resolve(dir);
|
|
147
|
-
if (!existsSync(full)) return [];
|
|
148
|
-
return readdirSync(full);
|
|
149
|
-
}
|
|
150
|
-
async mkdir(dir) {
|
|
151
|
-
mkdirSync(this.resolve(dir), { recursive: true });
|
|
152
|
-
}
|
|
153
|
-
async isDirectory(path) {
|
|
154
|
-
try {
|
|
155
|
-
return statSync(this.resolve(path)).isDirectory();
|
|
156
|
-
} catch {
|
|
157
|
-
return false;
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
// src/core/config.ts
|
|
163
|
-
function getConfigPath() {
|
|
164
|
-
const dataDir = process.env.CLAUDE_PLUGIN_DATA;
|
|
165
|
-
if (!dataDir) {
|
|
166
|
-
throw new Error("CLAUDE_PLUGIN_DATA environment variable is not set");
|
|
167
|
-
}
|
|
168
|
-
return join2(dataDir, "config.json");
|
|
169
|
-
}
|
|
170
|
-
function isOldConfig(raw) {
|
|
171
|
-
if (!raw || typeof raw !== "object") return false;
|
|
172
|
-
return "vaultPath" in raw && "reviewFolder" in raw;
|
|
173
|
-
}
|
|
174
|
-
function migrateOldConfig(old) {
|
|
175
|
-
return {
|
|
176
|
-
storage: {
|
|
177
|
-
type: "local",
|
|
178
|
-
local: {
|
|
179
|
-
basePath: join2(old.vaultPath, old.reviewFolder)
|
|
180
|
-
}
|
|
181
|
-
},
|
|
182
|
-
language: old.language,
|
|
183
|
-
periods: old.periods,
|
|
184
|
-
profile: old.profile
|
|
185
|
-
};
|
|
186
|
-
}
|
|
187
|
-
function loadConfig() {
|
|
188
|
-
const configPath = getConfigPath();
|
|
189
|
-
if (!existsSync2(configPath)) return null;
|
|
190
|
-
const raw = JSON.parse(readFileSync2(configPath, "utf-8"));
|
|
191
|
-
if (isOldConfig(raw)) {
|
|
192
|
-
const migrated = migrateOldConfig(raw);
|
|
193
|
-
saveConfig(migrated);
|
|
194
|
-
return migrated;
|
|
195
|
-
}
|
|
196
|
-
return raw;
|
|
197
|
-
}
|
|
198
|
-
function saveConfig(config) {
|
|
199
|
-
const configPath = getConfigPath();
|
|
200
|
-
mkdirSync2(dirname2(configPath), { recursive: true });
|
|
201
|
-
writeFileSync2(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
202
|
-
}
|
|
203
|
-
async function createStorageAdapter(config) {
|
|
204
|
-
if (config.storage.type === "local") {
|
|
205
|
-
return new LocalStorageAdapter(config.storage.local.basePath);
|
|
206
|
-
}
|
|
207
|
-
if (config.storage.type === "github") {
|
|
208
|
-
const { GitHubStorageAdapter: GitHubStorageAdapter2 } = await Promise.resolve().then(() => (init_github_storage(), github_storage_exports));
|
|
209
|
-
const g = config.storage.github;
|
|
210
|
-
return new GitHubStorageAdapter2(g.owner, g.repo, g.token, g.basePath);
|
|
211
|
-
}
|
|
212
|
-
throw new Error(`Unknown storage type: ${config.storage.type}`);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
// src/core/raw-logger.ts
|
|
216
|
-
function parseHookInput(raw) {
|
|
217
|
-
const parsed = JSON.parse(raw);
|
|
218
|
-
if (!parsed || typeof parsed !== "object") {
|
|
219
|
-
throw new Error("Invalid hook input: expected object");
|
|
220
|
-
}
|
|
221
|
-
if (typeof parsed.session_id !== "string" || !parsed.session_id) {
|
|
222
|
-
throw new Error("Invalid hook input: missing session_id");
|
|
223
|
-
}
|
|
224
|
-
return parsed;
|
|
225
|
-
}
|
|
226
|
-
async function appendRawLog(storage, sessionDir, date, entry) {
|
|
227
|
-
await storage.mkdir(sessionDir);
|
|
228
|
-
const logPath = `${sessionDir}/${date}.jsonl`;
|
|
229
|
-
const record = {
|
|
230
|
-
...entry,
|
|
231
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
232
|
-
};
|
|
233
|
-
await storage.append(logPath, JSON.stringify(record) + "\n");
|
|
234
|
-
}
|
|
235
|
-
|
|
236
|
-
// src/core/vault.ts
|
|
237
|
-
function getRawDir(sessionId) {
|
|
238
|
-
return `.raw/${sessionId}`;
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
// src/core/periods.ts
|
|
242
|
-
function formatDate(date) {
|
|
243
|
-
const y = date.getFullYear();
|
|
244
|
-
const m = String(date.getMonth() + 1).padStart(2, "0");
|
|
245
|
-
const d = String(date.getDate()).padStart(2, "0");
|
|
246
|
-
return `${y}-${m}-${d}`;
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
// src/hooks/on-stop.ts
|
|
250
|
-
async function handleStopHook(stdinData) {
|
|
251
|
-
try {
|
|
252
|
-
const config = loadConfig();
|
|
253
|
-
if (!config) return;
|
|
254
|
-
const storage = await createStorageAdapter(config);
|
|
255
|
-
const input = parseHookInput(stdinData);
|
|
256
|
-
const sessionDir = getRawDir(input.session_id);
|
|
257
|
-
const date = formatDate(/* @__PURE__ */ new Date());
|
|
258
|
-
await appendRawLog(storage, sessionDir, date, input);
|
|
259
|
-
} catch {
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
var isMainModule = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);
|
|
263
|
-
if (isMainModule) {
|
|
264
|
-
let data = "";
|
|
265
|
-
process.stdin.setEncoding("utf-8");
|
|
266
|
-
process.stdin.on("data", (chunk) => data += chunk);
|
|
267
|
-
process.stdin.on("end", () => {
|
|
268
|
-
handleStopHook(data);
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
export {
|
|
272
|
-
handleStopHook
|
|
273
|
-
};
|
|
274
|
-
//# sourceMappingURL=on-stop.js.map
|
package/dist/on-stop.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/github-storage.ts","../src/hooks/on-stop.ts","../src/core/config.ts","../src/core/local-storage.ts","../src/core/raw-logger.ts","../src/core/vault.ts","../src/core/periods.ts"],"sourcesContent":["import type { StorageAdapter } from \"./storage.js\";\n\nexport class GitHubStorageAdapter implements StorageAdapter {\n private baseUrl: string;\n private headers: Record<string, string>;\n\n constructor(\n private owner: string,\n private repo: string,\n private token: string,\n private basePath: string,\n ) {\n this.baseUrl = `https://api.github.com/repos/${owner}/${repo}/contents`;\n this.headers = {\n Authorization: `Bearer ${token}`,\n Accept: \"application/vnd.github.v3+json\",\n \"Content-Type\": \"application/json\",\n };\n }\n\n private getUrl(path: string): string {\n return `${this.baseUrl}/${this.basePath}/${path}`;\n }\n\n private async getSha(path: string): Promise<string | null> {\n const res = await fetch(this.getUrl(path), { method: \"GET\", headers: this.headers });\n if (res.status === 404) return null;\n const data = await res.json() as Record<string, unknown>;\n return (data.sha as string) || null;\n }\n\n async read(path: string): Promise<string | null> {\n const res = await fetch(this.getUrl(path), { method: \"GET\", headers: this.headers });\n if (res.status === 404) return null;\n const data = await res.json() as Record<string, unknown>;\n const content = data.content as string;\n return Buffer.from(content, \"base64\").toString(\"utf-8\");\n }\n\n async write(path: string, content: string): Promise<void> {\n const sha = await this.getSha(path);\n const body: Record<string, unknown> = {\n message: `update ${path}`,\n content: Buffer.from(content).toString(\"base64\"),\n };\n if (sha) body.sha = sha;\n\n const res = await fetch(this.getUrl(path), {\n method: \"PUT\",\n headers: this.headers,\n body: JSON.stringify(body),\n });\n\n if (!res.ok && res.status === 409) {\n const freshSha = await this.getSha(path);\n if (freshSha) body.sha = freshSha;\n await fetch(this.getUrl(path), {\n method: \"PUT\",\n headers: this.headers,\n body: JSON.stringify(body),\n });\n }\n }\n\n async append(path: string, content: string): Promise<void> {\n const existing = await this.read(path);\n const newContent = existing ? existing + content : content;\n await this.write(path, newContent);\n }\n\n async exists(path: string): Promise<boolean> {\n const res = await fetch(this.getUrl(path), { method: \"GET\", headers: this.headers });\n return res.status !== 404;\n }\n\n async list(dir: string): Promise<string[]> {\n const res = await fetch(this.getUrl(dir), { method: \"GET\", headers: this.headers });\n if (res.status === 404) return [];\n const data = await res.json() as Array<{ name: string }>;\n if (!Array.isArray(data)) return [];\n return data.map((entry) => entry.name);\n }\n\n async mkdir(_dir: string): Promise<void> {\n // GitHub creates directories implicitly when files are created\n }\n\n async isDirectory(path: string): Promise<boolean> {\n const res = await fetch(this.getUrl(path), { method: \"GET\", headers: this.headers });\n if (res.status === 404) return false;\n const data = await res.json();\n return Array.isArray(data);\n }\n}\n","// src/hooks/on-stop.ts\nimport { fileURLToPath } from \"url\";\nimport { resolve } from \"path\";\nimport { loadConfig, createStorageAdapter } from \"../core/config.js\";\nimport { parseHookInput, appendRawLog } from \"../core/raw-logger.js\";\nimport { getRawDir } from \"../core/vault.js\";\nimport { formatDate } from \"../core/periods.js\";\n\nexport async function handleStopHook(stdinData: string): Promise<void> {\n try {\n const config = loadConfig();\n if (!config) return;\n\n const storage = await createStorageAdapter(config);\n const input = parseHookInput(stdinData);\n const sessionDir = getRawDir(input.session_id);\n const date = formatDate(new Date());\n\n await appendRawLog(storage, sessionDir, date, input);\n } catch {\n // async hook — fail silently\n }\n}\n\n// Main execution\nconst isMainModule = process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1]);\n\nif (isMainModule) {\n let data = \"\";\n process.stdin.setEncoding(\"utf-8\");\n process.stdin.on(\"data\", (chunk) => (data += chunk));\n process.stdin.on(\"end\", () => {\n handleStopHook(data);\n });\n}\n","import { readFileSync, writeFileSync, existsSync, mkdirSync } from \"fs\";\nimport { dirname, join } from \"path\";\nimport type { StorageAdapter } from \"./storage.js\";\nimport { LocalStorageAdapter } from \"./local-storage.js\";\n\nexport interface Profile {\n company: string;\n role: string;\n team: string;\n context: string;\n}\n\nexport interface Periods {\n daily: true;\n weekly: boolean;\n monthly: boolean;\n quarterly: boolean;\n yearly: boolean;\n}\n\nexport interface LocalStorageConfig {\n basePath: string;\n}\n\nexport interface GitHubStorageConfig {\n owner: string;\n repo: string;\n token: string;\n basePath: string;\n}\n\nexport interface StorageConfig {\n type: \"local\" | \"github\";\n local?: LocalStorageConfig;\n github?: GitHubStorageConfig;\n}\n\nexport interface Config {\n storage: StorageConfig;\n language: string;\n periods: Periods;\n profile: Profile;\n}\n\ninterface OldConfig {\n vaultPath: string;\n reviewFolder: string;\n language: string;\n periods: Periods;\n profile: Profile;\n}\n\nconst DEFAULT_PERIODS: Periods = {\n daily: true,\n weekly: true,\n monthly: true,\n quarterly: true,\n yearly: false,\n};\n\nconst DEFAULT_PROFILE: Profile = {\n company: \"\",\n role: \"\",\n team: \"\",\n context: \"\",\n};\n\nexport function getConfigPath(): string {\n const dataDir = process.env.CLAUDE_PLUGIN_DATA;\n if (!dataDir) {\n throw new Error(\"CLAUDE_PLUGIN_DATA environment variable is not set\");\n }\n return join(dataDir, \"config.json\");\n}\n\nfunction isOldConfig(raw: unknown): raw is OldConfig {\n if (!raw || typeof raw !== \"object\") return false;\n return \"vaultPath\" in raw && \"reviewFolder\" in raw;\n}\n\nfunction migrateOldConfig(old: OldConfig): Config {\n return {\n storage: {\n type: \"local\",\n local: {\n basePath: join(old.vaultPath, old.reviewFolder),\n },\n },\n language: old.language,\n periods: old.periods,\n profile: old.profile,\n };\n}\n\nexport function loadConfig(): Config | null {\n const configPath = getConfigPath();\n if (!existsSync(configPath)) return null;\n const raw = JSON.parse(readFileSync(configPath, \"utf-8\"));\n if (isOldConfig(raw)) {\n const migrated = migrateOldConfig(raw);\n saveConfig(migrated);\n return migrated;\n }\n return raw as Config;\n}\n\nexport function saveConfig(config: Config): void {\n const configPath = getConfigPath();\n mkdirSync(dirname(configPath), { recursive: true });\n writeFileSync(configPath, JSON.stringify(config, null, 2), \"utf-8\");\n}\n\nexport function validateConfig(config: unknown): config is Config {\n if (!config || typeof config !== \"object\") return false;\n const c = config as Record<string, unknown>;\n if (!c.storage || typeof c.storage !== \"object\") return false;\n const s = c.storage as Record<string, unknown>;\n if (s.type !== \"local\" && s.type !== \"github\") return false;\n if (s.type === \"local\") {\n if (!s.local || typeof s.local !== \"object\") return false;\n const l = s.local as Record<string, unknown>;\n if (typeof l.basePath !== \"string\" || l.basePath === \"\") return false;\n }\n if (s.type === \"github\") {\n if (!s.github || typeof s.github !== \"object\") return false;\n const g = s.github as Record<string, unknown>;\n if (typeof g.owner !== \"string\" || !g.owner) return false;\n if (typeof g.repo !== \"string\" || !g.repo) return false;\n if (typeof g.token !== \"string\" || !g.token) return false;\n }\n return true;\n}\n\nexport function createDefaultLocalConfig(basePath: string): Config {\n return {\n storage: { type: \"local\", local: { basePath } },\n language: \"ko\",\n periods: { ...DEFAULT_PERIODS },\n profile: { ...DEFAULT_PROFILE },\n };\n}\n\nexport function createDefaultGitHubConfig(owner: string, repo: string, token: string): Config {\n return {\n storage: { type: \"github\", github: { owner, repo, token, basePath: \"daily-review\" } },\n language: \"ko\",\n periods: { ...DEFAULT_PERIODS },\n profile: { ...DEFAULT_PROFILE },\n };\n}\n\nexport async function createStorageAdapter(config: Config): Promise<StorageAdapter> {\n if (config.storage.type === \"local\") {\n return new LocalStorageAdapter(config.storage.local!.basePath);\n }\n if (config.storage.type === \"github\") {\n const { GitHubStorageAdapter } = await import(\"./github-storage.js\");\n const g = config.storage.github!;\n return new GitHubStorageAdapter(g.owner, g.repo, g.token, g.basePath);\n }\n throw new Error(`Unknown storage type: ${(config.storage as any).type}`);\n}\n","import {\n readFileSync,\n writeFileSync,\n appendFileSync,\n existsSync,\n mkdirSync,\n readdirSync,\n statSync,\n} from \"fs\";\nimport { dirname, join } from \"path\";\nimport type { StorageAdapter } from \"./storage.js\";\n\nexport class LocalStorageAdapter implements StorageAdapter {\n constructor(private basePath: string) {}\n\n private resolve(path: string): string {\n return join(this.basePath, path);\n }\n\n async read(path: string): Promise<string | null> {\n const full = this.resolve(path);\n if (!existsSync(full)) return null;\n return readFileSync(full, \"utf-8\");\n }\n\n async write(path: string, content: string): Promise<void> {\n const full = this.resolve(path);\n mkdirSync(dirname(full), { recursive: true });\n writeFileSync(full, content, \"utf-8\");\n }\n\n async append(path: string, content: string): Promise<void> {\n const full = this.resolve(path);\n mkdirSync(dirname(full), { recursive: true });\n appendFileSync(full, content, \"utf-8\");\n }\n\n async exists(path: string): Promise<boolean> {\n return existsSync(this.resolve(path));\n }\n\n async list(dir: string): Promise<string[]> {\n const full = this.resolve(dir);\n if (!existsSync(full)) return [];\n return readdirSync(full);\n }\n\n async mkdir(dir: string): Promise<void> {\n mkdirSync(this.resolve(dir), { recursive: true });\n }\n\n async isDirectory(path: string): Promise<boolean> {\n try {\n return statSync(this.resolve(path)).isDirectory();\n } catch {\n return false;\n }\n }\n}\n","// src/core/raw-logger.ts\nimport type { StorageAdapter } from \"./storage.js\";\n\nexport interface HookInput {\n session_id: string;\n transcript_path: string;\n cwd: string;\n hook_event_name: string;\n [key: string]: unknown;\n}\n\nexport function parseHookInput(raw: string): HookInput {\n const parsed = JSON.parse(raw);\n if (!parsed || typeof parsed !== \"object\") {\n throw new Error(\"Invalid hook input: expected object\");\n }\n if (typeof parsed.session_id !== \"string\" || !parsed.session_id) {\n throw new Error(\"Invalid hook input: missing session_id\");\n }\n return parsed as HookInput;\n}\n\nexport async function appendRawLog(storage: StorageAdapter, sessionDir: string, date: string, entry: HookInput): Promise<void> {\n await storage.mkdir(sessionDir);\n const logPath = `${sessionDir}/${date}.jsonl`;\n const record = {\n ...entry,\n timestamp: new Date().toISOString(),\n };\n await storage.append(logPath, JSON.stringify(record) + \"\\n\");\n}\n","// src/core/vault.ts\nimport type { StorageAdapter } from \"./storage.js\";\nimport type { Periods } from \"./config.js\";\n\nexport function getRawDir(sessionId: string): string {\n return `.raw/${sessionId}`;\n}\n\nexport function getReviewsDir(): string {\n return \".reviews\";\n}\n\nexport function getDailyPath(date: string): string {\n return `daily/${date}.md`;\n}\n\nexport function getWeeklyPath(week: string): string {\n return `weekly/${week}.md`;\n}\n\nexport function getMonthlyPath(month: string): string {\n return `monthly/${month}.md`;\n}\n\nexport function getQuarterlyPath(quarter: string): string {\n return `quarterly/${quarter}.md`;\n}\n\nexport function getYearlyPath(year: string): string {\n return `yearly/${year}.md`;\n}\n\nexport function getProjectDailyPath(projectName: string, date: string): string {\n return `projects/${projectName}/${date}.md`;\n}\n\nexport function getProjectSummaryPath(projectName: string): string {\n return `projects/${projectName}/summary.md`;\n}\n\nexport function getUncategorizedPath(date: string): string {\n return `uncategorized/${date}.md`;\n}\n\nexport async function ensureVaultDirectories(storage: StorageAdapter, periods: Periods): Promise<void> {\n const dirs = [\"daily\", \"projects\", \"uncategorized\", \".raw\", \".reviews\"];\n if (periods.weekly) dirs.push(\"weekly\");\n if (periods.monthly) dirs.push(\"monthly\");\n if (periods.quarterly) dirs.push(\"quarterly\");\n if (periods.yearly) dirs.push(\"yearly\");\n\n for (const dir of dirs) {\n await storage.mkdir(dir);\n }\n}\n","export function getISOWeek(date: Date): number {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));\n const dayNum = d.getUTCDay() || 7;\n d.setUTCDate(d.getUTCDate() + 4 - dayNum);\n const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));\n return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);\n}\n\nexport function getISOWeekYear(date: Date): number {\n const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));\n const dayNum = d.getUTCDay() || 7;\n d.setUTCDate(d.getUTCDate() + 4 - dayNum);\n return d.getUTCFullYear();\n}\n\nexport function getQuarter(date: Date): number {\n return Math.ceil((date.getMonth() + 1) / 3);\n}\n\nexport function formatDate(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n}\n\nexport function formatWeek(date: Date): string {\n return `${getISOWeekYear(date)}-W${String(getISOWeek(date)).padStart(2, \"0\")}`;\n}\n\nexport function formatMonth(date: Date): string {\n return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, \"0\")}`;\n}\n\nexport function formatQuarter(date: Date): string {\n return `${date.getFullYear()}-Q${getQuarter(date)}`;\n}\n\nexport function formatYear(date: Date): string {\n return `${date.getFullYear()}`;\n}\n\nexport interface PeriodCheck {\n needsWeekly: boolean;\n needsMonthly: boolean;\n needsQuarterly: boolean;\n needsYearly: boolean;\n previousWeek: string;\n previousMonth: string;\n previousQuarter: string;\n previousYear: string;\n}\n\nexport function checkPeriodsNeeded(today: Date, lastRun: Date | null): PeriodCheck {\n if (!lastRun) {\n return {\n needsWeekly: false,\n needsMonthly: false,\n needsQuarterly: false,\n needsYearly: false,\n previousWeek: \"\",\n previousMonth: \"\",\n previousQuarter: \"\",\n previousYear: \"\",\n };\n }\n\n const todayWeek = formatWeek(today);\n const lastWeek = formatWeek(lastRun);\n const todayMonth = formatMonth(today);\n const lastMonth = formatMonth(lastRun);\n const todayQuarter = formatQuarter(today);\n const lastQuarter = formatQuarter(lastRun);\n const todayYear = formatYear(today);\n const lastYear = formatYear(lastRun);\n\n return {\n needsWeekly: todayWeek !== lastWeek,\n needsMonthly: todayMonth !== lastMonth,\n needsQuarterly: todayQuarter !== lastQuarter,\n needsYearly: todayYear !== lastYear,\n previousWeek: lastWeek,\n previousMonth: lastMonth,\n previousQuarter: lastQuarter,\n previousYear: lastYear,\n };\n}\n"],"mappings":"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","names":["readFileSync","writeFileSync","existsSync","mkdirSync","dirname","join","join","existsSync","readFileSync","mkdirSync","dirname","writeFileSync","GitHubStorageAdapter"]}
|