@giwonn/claude-daily-review 0.3.2 → 0.3.4
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 +1 -1
- package/.github/workflows/release.yml +69 -0
- package/README.ko.md +28 -13
- package/README.md +38 -21
- package/commands/daily-review-setup.md +136 -76
- package/commands/generate.md +195 -0
- package/hooks/hooks.json +6 -11
- package/hooks/on-stop.mjs +58 -2
- package/hooks/recover-sessions.mjs +179 -0
- package/hooks/run-hook.cmd +0 -0
- package/hooks/session-start-check +1 -9
- package/lib/check-config.mjs +8 -0
- package/lib/collect-raw-logs.mjs +153 -0
- package/lib/config.mjs +1 -1
- package/lib/create-dirs.mjs +13 -0
- package/lib/github-storage.mjs +1 -1
- package/lib/poll-token.mjs +5 -0
- package/lib/raw-logger.mjs +16 -2
- package/lib/request-device-code.mjs +4 -0
- package/package.json +1 -1
- package/.github/workflows/publish.yml +0 -24
- package/.github/workflows/update-marketplace.yml +0 -28
- package/prompts/session-end.md +0 -233
- package/prompts/session-start.md +0 -358
package/lib/github-storage.mjs
CHANGED
|
@@ -15,7 +15,7 @@ export class GitHubStorageAdapter {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
/** @private @param {string} path @returns {string} */
|
|
18
|
-
getUrl(path) { return `${this.baseUrl}/${this.basePath}/${path}`; }
|
|
18
|
+
getUrl(path) { return this.basePath ? `${this.baseUrl}/${this.basePath}/${path}` : `${this.baseUrl}/${path}`; }
|
|
19
19
|
|
|
20
20
|
/** @private @param {string} path @returns {Promise<string | null>} */
|
|
21
21
|
async getSha(path) {
|
package/lib/raw-logger.mjs
CHANGED
|
@@ -14,6 +14,20 @@ export function parseHookInput(raw) {
|
|
|
14
14
|
export async function appendRawLog(storage, sessionDir, date, entry) {
|
|
15
15
|
await storage.mkdir(sessionDir);
|
|
16
16
|
const logPath = `${sessionDir}/${date}.jsonl`;
|
|
17
|
-
const
|
|
18
|
-
|
|
17
|
+
const now = new Date().toISOString();
|
|
18
|
+
let lines = '';
|
|
19
|
+
|
|
20
|
+
// User message row (with original question timestamp)
|
|
21
|
+
if (entry.last_user_message) {
|
|
22
|
+
lines += JSON.stringify({ type: 'user', message: entry.last_user_message, session_id: entry.session_id, cwd: entry.cwd, timestamp: entry.user_timestamp || now }) + '\n';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Assistant message row (with response completion timestamp)
|
|
26
|
+
if (entry.last_assistant_message) {
|
|
27
|
+
lines += JSON.stringify({ type: 'assistant', message: entry.last_assistant_message, session_id: entry.session_id, cwd: entry.cwd, timestamp: now }) + '\n';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (lines) {
|
|
31
|
+
await storage.append(logPath, lines);
|
|
32
|
+
}
|
|
19
33
|
}
|
package/package.json
CHANGED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
name: Publish to npm
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
publish:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
environment: npm
|
|
11
|
-
permissions:
|
|
12
|
-
contents: read
|
|
13
|
-
id-token: write
|
|
14
|
-
steps:
|
|
15
|
-
- uses: actions/checkout@v4
|
|
16
|
-
|
|
17
|
-
- uses: actions/setup-node@v4
|
|
18
|
-
with:
|
|
19
|
-
node-version: "22"
|
|
20
|
-
registry-url: "https://registry.npmjs.org"
|
|
21
|
-
|
|
22
|
-
- run: npm install -g npm@latest
|
|
23
|
-
|
|
24
|
-
- run: npm publish --access public --provenance
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
name: Update Marketplace SHA
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
release:
|
|
5
|
-
types: [published]
|
|
6
|
-
|
|
7
|
-
jobs:
|
|
8
|
-
update-sha:
|
|
9
|
-
runs-on: ubuntu-latest
|
|
10
|
-
permissions:
|
|
11
|
-
contents: write
|
|
12
|
-
steps:
|
|
13
|
-
- uses: actions/checkout@v4
|
|
14
|
-
with:
|
|
15
|
-
ref: master
|
|
16
|
-
|
|
17
|
-
- name: Update SHA in marketplace.json
|
|
18
|
-
run: |
|
|
19
|
-
SHA=$(git rev-parse HEAD)
|
|
20
|
-
sed -i "s/\"sha\": \"[a-f0-9]*\"/\"sha\": \"$SHA\"/" .claude-plugin/marketplace.json
|
|
21
|
-
cat .claude-plugin/marketplace.json
|
|
22
|
-
|
|
23
|
-
- name: Commit and push
|
|
24
|
-
run: |
|
|
25
|
-
git config user.name "github-actions[bot]"
|
|
26
|
-
git config user.email "github-actions[bot]@users.noreply.github.com"
|
|
27
|
-
git add .claude-plugin/marketplace.json
|
|
28
|
-
git diff --cached --quiet && echo "No changes" || (git commit -m "chore: update marketplace SHA [skip ci]" && git push)
|
package/prompts/session-end.md
DELETED
|
@@ -1,233 +0,0 @@
|
|
|
1
|
-
# SessionEnd Agent Prompt — claude-daily-review
|
|
2
|
-
|
|
3
|
-
You are a session review generator for the claude-daily-review plugin. Your job is to analyze a Claude Code conversation transcript and produce a structured review markdown file. This review will later be merged into a daily review document.
|
|
4
|
-
|
|
5
|
-
## Storage Abstraction
|
|
6
|
-
|
|
7
|
-
This plugin supports two storage backends: **local** and **github**. After reading the config, determine the storage type and use the appropriate method for all file operations throughout this prompt.
|
|
8
|
-
|
|
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
|
-
- **If `storage.type === "github"`:** Use the storage-cli tool via Bash for all file operations. The CLI commands are:
|
|
11
|
-
```bash
|
|
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
|
-
```
|
|
18
|
-
All `<path>` arguments are relative to the configured `storage.github.basePath` (e.g., `daily-review`). The CLI handles GitHub API calls internally.
|
|
19
|
-
|
|
20
|
-
In all subsequent steps, when the prompt says "write a file to `{path}`" or "read the file at `{path}`", use the method matching the storage type. For local storage, the full path is `{storage.local.basePath}/{path}`. For GitHub storage, pass `{path}` directly to the storage-cli.
|
|
21
|
-
|
|
22
|
-
## Step 1: Read Configuration
|
|
23
|
-
|
|
24
|
-
Read the config file at `$CLAUDE_PLUGIN_DATA/config.json`. Parse it as JSON. The structure is:
|
|
25
|
-
|
|
26
|
-
```json
|
|
27
|
-
{
|
|
28
|
-
"storage": {
|
|
29
|
-
"type": "local",
|
|
30
|
-
"local": { "basePath": "/path/to/vault/daily-review" }
|
|
31
|
-
},
|
|
32
|
-
"language": "ko",
|
|
33
|
-
"periods": { "daily": true, "weekly": true, "monthly": true, "quarterly": true, "yearly": false },
|
|
34
|
-
"profile": {
|
|
35
|
-
"company": "ABC Corp",
|
|
36
|
-
"role": "프론트엔드 개발자",
|
|
37
|
-
"team": "결제플랫폼팀",
|
|
38
|
-
"context": "B2B SaaS 결제 시스템 개발 및 운영"
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
Or for GitHub storage:
|
|
44
|
-
```json
|
|
45
|
-
{
|
|
46
|
-
"storage": {
|
|
47
|
-
"type": "github",
|
|
48
|
-
"github": { "owner": "user", "repo": "repo", "token": "tok", "basePath": "daily-review" }
|
|
49
|
-
},
|
|
50
|
-
"language": "ko",
|
|
51
|
-
"periods": { ... },
|
|
52
|
-
"profile": { ... }
|
|
53
|
-
}
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
If the config file does not exist or cannot be read, write an error to stderr and exit with code 2.
|
|
57
|
-
|
|
58
|
-
Determine the storage type from `config.storage.type` and use the appropriate file operation method for all remaining steps.
|
|
59
|
-
|
|
60
|
-
## Step 2: Read Hook Input
|
|
61
|
-
|
|
62
|
-
Read stdin as JSON. It contains:
|
|
63
|
-
|
|
64
|
-
```json
|
|
65
|
-
{
|
|
66
|
-
"session_id": "abc123",
|
|
67
|
-
"transcript_path": "/path/to/transcript.jsonl",
|
|
68
|
-
"cwd": "/home/user/projects/my-app"
|
|
69
|
-
}
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
- `session_id`: Unique identifier for this session
|
|
73
|
-
- `transcript_path`: Path to the JSONL transcript file containing the full conversation
|
|
74
|
-
- `cwd`: Working directory where Claude Code was running
|
|
75
|
-
|
|
76
|
-
## Step 3: Read and Parse Transcript
|
|
77
|
-
|
|
78
|
-
Read the file at `transcript_path`. It is in JSONL format (one JSON object per line). Each line represents a conversation turn. Parse all lines and reconstruct the conversation flow.
|
|
79
|
-
|
|
80
|
-
If the transcript file is empty or cannot be read, write `.completed` marker to the raw session directory and exit gracefully — there is nothing to review.
|
|
81
|
-
|
|
82
|
-
## Step 4: Analyze and Classify
|
|
83
|
-
|
|
84
|
-
Classify the conversation content into groups:
|
|
85
|
-
|
|
86
|
-
### Project Detection
|
|
87
|
-
|
|
88
|
-
Derive the project name from `cwd`:
|
|
89
|
-
- Extract the last path component as the project name (e.g., `/home/user/projects/my-app` -> `my-app`)
|
|
90
|
-
- If cwd is a home directory or root, classify as "uncategorized"
|
|
91
|
-
|
|
92
|
-
### Content Grouping
|
|
93
|
-
|
|
94
|
-
Within each project group, identify distinct work topics. A topic is a coherent unit of work (e.g., "authentication refactoring", "bug fix in payment module"). Group related Q&A and discussion under the same topic.
|
|
95
|
-
|
|
96
|
-
### What to Extract
|
|
97
|
-
|
|
98
|
-
For each topic, extract:
|
|
99
|
-
1. **작업 요약 (Work Summary):** What was done, in 1-2 concise sentences. Frame with business context using the profile information.
|
|
100
|
-
2. **배운 것 (Learnings):** Technical insights, new patterns, API behaviors discovered. Bullet points.
|
|
101
|
-
3. **고민한 포인트 (Decision Points):** Decisions made and their reasoning. Format: "X vs Y -> chose X (reason)"
|
|
102
|
-
4. **질문과 답변 (Q&A):** Key questions asked and their answers. Not raw conversation — distill to the essential knowledge.
|
|
103
|
-
|
|
104
|
-
If profile information is available, use it to frame summaries with business context. For example, instead of "implemented JWT auth", write "B2B SaaS 멀티테넌트 환경에서 JWT 기반 인증 설계 및 구현" if that matches the profile context.
|
|
105
|
-
|
|
106
|
-
### What to Skip
|
|
107
|
-
|
|
108
|
-
- Trivial exchanges ("thanks", "ok", small talk)
|
|
109
|
-
- Repeated or redundant information
|
|
110
|
-
- Raw code dumps — summarize what the code does instead
|
|
111
|
-
- Debugging back-and-forth — summarize the root cause and fix
|
|
112
|
-
|
|
113
|
-
## Step 5: Generate Review Markdown
|
|
114
|
-
|
|
115
|
-
Write the review to: `.reviews/{session_id}.md` (using the appropriate storage method)
|
|
116
|
-
|
|
117
|
-
Use the configured `language` for all generated text. If language is "ko", write in Korean. If "en", write in English. Etc.
|
|
118
|
-
|
|
119
|
-
### Review File Format
|
|
120
|
-
|
|
121
|
-
```markdown
|
|
122
|
-
---
|
|
123
|
-
date: {YYYY-MM-DD}
|
|
124
|
-
type: session-review
|
|
125
|
-
session_id: {session_id}
|
|
126
|
-
projects: [{project-names}]
|
|
127
|
-
tags: [{technology-tags}]
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
## [{project-name}] {topic-title}
|
|
131
|
-
**작업 요약:** {concise summary with business context}
|
|
132
|
-
**배운 것:**
|
|
133
|
-
- {learning 1}
|
|
134
|
-
- {learning 2}
|
|
135
|
-
**고민한 포인트:**
|
|
136
|
-
- {decision}: {option A} vs {option B} → {chosen} ({reason})
|
|
137
|
-
**질문과 답변:**
|
|
138
|
-
- Q: {distilled question}
|
|
139
|
-
→ A: {concise answer}
|
|
140
|
-
|
|
141
|
-
## [{project-name}] {another-topic}
|
|
142
|
-
...
|
|
143
|
-
|
|
144
|
-
## 미분류
|
|
145
|
-
**질문과 답변:**
|
|
146
|
-
- Q: {question}
|
|
147
|
-
→ A: {answer}
|
|
148
|
-
|
|
149
|
-
## Tags
|
|
150
|
-
#{project-name} #{technology1} #{technology2}
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
### Important Formatting Rules
|
|
154
|
-
|
|
155
|
-
- Use Obsidian-compatible tags: `#project-name #technology`
|
|
156
|
-
- Project names in square brackets: `[my-app]`
|
|
157
|
-
- Use `→` for answer indicators in Q&A
|
|
158
|
-
- Omit sections that have no content (e.g., if there are no decision points, skip 고민한 포인트)
|
|
159
|
-
- If the entire session is uncategorized, omit project sections and only include the 미분류 section
|
|
160
|
-
- Include the YAML frontmatter with date, type, session_id, projects list, and tags list
|
|
161
|
-
|
|
162
|
-
### Section Labels by Language
|
|
163
|
-
|
|
164
|
-
| Section | ko | en |
|
|
165
|
-
|---------|----|----|
|
|
166
|
-
| Work Summary | 작업 요약 | Work Summary |
|
|
167
|
-
| Learnings | 배운 것 | Learnings |
|
|
168
|
-
| Decision Points | 고민한 포인트 | Decision Points |
|
|
169
|
-
| Q&A | 질문과 답변 | Q&A |
|
|
170
|
-
| Uncategorized | 미분류 | Uncategorized |
|
|
171
|
-
| Tags | Tags | Tags |
|
|
172
|
-
|
|
173
|
-
## Step 6: Update Project Summary (if applicable)
|
|
174
|
-
|
|
175
|
-
If the session involved project work (not just uncategorized), update the project summary file at:
|
|
176
|
-
`projects/{project-name}/summary.md` (using the appropriate storage method)
|
|
177
|
-
|
|
178
|
-
- If the file exists, read it and append/update relevant sections
|
|
179
|
-
- If the file does not exist, create it with this template:
|
|
180
|
-
|
|
181
|
-
```markdown
|
|
182
|
-
---
|
|
183
|
-
project: {project-name}
|
|
184
|
-
type: project-summary
|
|
185
|
-
started: {today's date}
|
|
186
|
-
last-updated: {today's date}
|
|
187
|
-
tags: [{technology-tags}]
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
# {project-name} 프로젝트 요약
|
|
191
|
-
|
|
192
|
-
## 프로젝트 개요
|
|
193
|
-
{inferred from conversation context and profile}
|
|
194
|
-
|
|
195
|
-
## 기술 스택
|
|
196
|
-
- {technologies used}
|
|
197
|
-
|
|
198
|
-
## 주요 구현 사항
|
|
199
|
-
- {what was implemented today}
|
|
200
|
-
|
|
201
|
-
## 핵심 의사결정 로그
|
|
202
|
-
- {date}: {decision} → {choice} ({reason})
|
|
203
|
-
|
|
204
|
-
## 배운 것 (누적)
|
|
205
|
-
- {learnings from this session}
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
When updating an existing summary:
|
|
209
|
-
- Update `last-updated` in frontmatter
|
|
210
|
-
- Add new tags if any
|
|
211
|
-
- Append new implementation items to 주요 구현 사항
|
|
212
|
-
- Append new decisions to 핵심 의사결정 로그
|
|
213
|
-
- Append new learnings to 배운 것 (avoid duplicates)
|
|
214
|
-
|
|
215
|
-
## Step 7: Mark Session as Completed
|
|
216
|
-
|
|
217
|
-
Write a `.completed` marker file to the raw session directory:
|
|
218
|
-
`.raw/{session_id}/.completed` (using the appropriate storage method)
|
|
219
|
-
|
|
220
|
-
The content of the marker file should be the current ISO timestamp (e.g., `2026-03-28T15:30:00.000Z`).
|
|
221
|
-
|
|
222
|
-
This marker prevents the session from being reprocessed during recovery.
|
|
223
|
-
|
|
224
|
-
## Critical Rules
|
|
225
|
-
|
|
226
|
-
1. **Be concise but meaningful.** These reviews serve as career documentation. Every line should be worth reading months later.
|
|
227
|
-
2. **Extract insights, not conversations.** Transform raw dialogue into structured knowledge.
|
|
228
|
-
3. **Use profile context.** If the user works on "B2B SaaS 결제 시스템", frame summaries in that context.
|
|
229
|
-
4. **Use the configured language** for all generated content (section headers, summaries, etc.).
|
|
230
|
-
5. **Create directories as needed.** Use `mkdir -p` equivalent before writing any file.
|
|
231
|
-
6. **Never delete raw data.** Only add the `.completed` marker — never remove or modify `.raw/` files.
|
|
232
|
-
7. **If the transcript is trivial** (very short session, no substantive work), still write a minimal review and mark as completed. Do not skip the session.
|
|
233
|
-
8. **Obsidian compatibility.** Use valid markdown, proper YAML frontmatter, and Obsidian-style tags.
|