@tekmidian/pai 0.7.0 → 0.7.2

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.
@@ -0,0 +1,232 @@
1
+ ---
2
+ name: Reconstruct
3
+ description: "Retroactively create session notes from JSONL transcripts and git history when automatic capture failed or was incomplete. USE WHEN user says 'reconstruct sessions', 'rebuild session notes', 'recover session notes', 'retroactively create notes', 'create notes from git history', 'notes are missing', 'backfill session notes', 'reconstruct what we did', OR /reconstruct."
4
+ ---
5
+
6
+ ## Reconstruct Skill
7
+
8
+ USE WHEN user says 'reconstruct sessions', 'rebuild session notes', 'recover session notes', 'retroactively create notes', 'create notes from git history', 'notes are missing', 'backfill session notes', 'reconstruct what we did', OR /reconstruct.
9
+
10
+ ### What This Skill Does
11
+
12
+ Reconstructs session notes for a PAI-registered project by reading:
13
+ 1. Git commit history (authoritative record of what was built)
14
+ 2. JSONL transcripts (user messages reveal intent, decisions, and context)
15
+
16
+ Generates one session note per day (or per logical session if multiple sessions existed in a day), numbered sequentially from the highest existing note number.
17
+
18
+ **ONLY include what can be verified from git log and JSONL. Never invent or embellish content.**
19
+
20
+ ---
21
+
22
+ ### Arguments
23
+
24
+ | Argument | Default | Description |
25
+ |----------|---------|-------------|
26
+ | `--days N` | 7 | Reconstruct notes for the last N days |
27
+ | `--since YYYY-MM-DD` | — | Reconstruct from this date forward |
28
+ | `--until YYYY-MM-DD` | today | Reconstruct up to this date |
29
+ | `--dry-run` | false | Show what would be created without writing files |
30
+ | `--commit` | false | Git commit the created notes after writing |
31
+
32
+ ---
33
+
34
+ ### Pre-Action Check
35
+
36
+ Before starting, verify:
37
+
38
+ 1. **Project is registered**: Run `pai project detect` (or `pai project list`) to confirm the current project is known to PAI and get its Notes directory path.
39
+ 2. **Notes directory exists**: The target directory should be `Notes/YYYY/MM/`. Create it if absent.
40
+ 3. **Find highest existing note number**: Scan all `Notes/**/*.md` files for the pattern `^NNNN` to find max note number. New notes continue from there.
41
+ 4. **NEVER overwrite existing notes**: If a note file already exists for that date/session, skip it and warn the user.
42
+
43
+ ---
44
+
45
+ ### Step 1 — Find JSONL Files
46
+
47
+ Claude Code encodes project paths lossily: `/`, spaces, dots, hyphens all become `-`. A project may have been accessed from multiple base directories (e.g., `~/Daten/Cloud/Development/ai/PAI` and `~/dev/ai/PAI` both encode to different paths).
48
+
49
+ **Search strategy — use glob patterns, not exact paths:**
50
+
51
+ ```bash
52
+ # Find all encoded project directories that might match this project
53
+ ls ~/.claude/projects/ | grep -i "<project-slug-fragment>"
54
+
55
+ # For each candidate, look for JSONL files
56
+ ls ~/.claude/projects/<encoded-path>/*.jsonl 2>/dev/null
57
+
58
+ # Also check the dev copy if the project has one
59
+ # Example: if cwd is ~/dev/ai/PAI, also check ~/Daten/Cloud/Development/ai/PAI encoding
60
+ ```
61
+
62
+ Collect ALL JSONL files from ALL matching encoded paths. A session may have been started from either location.
63
+
64
+ ---
65
+
66
+ ### Step 2 — Determine Time Range
67
+
68
+ Calculate the date range from arguments:
69
+ - `--days 7` → from (today - 7 days) to today
70
+ - `--since 2026-03-01` → from that date to today (or --until date)
71
+ - Default: last 7 days
72
+
73
+ ---
74
+
75
+ ### Step 3 — Extract Git History
76
+
77
+ For each day in the range:
78
+
79
+ ```bash
80
+ git -C <project-dir> log \\
81
+ --after="YYYY-MM-DD 00:00:00" \\
82
+ --before="YYYY-MM-DD 23:59:59" \\
83
+ --format="%H|%ad|%s" \\
84
+ --date=short \\
85
+ --stat
86
+ ```
87
+
88
+ Group commits by day. If no commits on a day, skip that day (no note to reconstruct).
89
+
90
+ Also capture:
91
+ - Files changed per commit (`--stat`)
92
+ - Author date (not committer date) for accurate day grouping
93
+
94
+ ---
95
+
96
+ ### Step 4 — Extract User Messages from JSONL
97
+
98
+ For each JSONL file, parse messages where `type == "user"` AND `message.role == "user"`:
99
+
100
+ ```
101
+ Each line is a JSON object. Look for:
102
+ {
103
+ "type": "user",
104
+ "message": {
105
+ "role": "user",
106
+ "content": "..." | [{"type": "text", "text": "..."}]
107
+ },
108
+ "timestamp": "2026-03-15T10:23:45.000Z"
109
+ }
110
+ ```
111
+
112
+ Filter messages by timestamp to match the day being reconstructed.
113
+
114
+ Extract:
115
+ - The text content of user messages (ignore tool_result entries)
116
+ - Timestamps for ordering
117
+ - Session ID (`sessionId` field) to group messages per session
118
+
119
+ **SKIP** messages that are:
120
+ - Tool results (`content` is array with `type: "tool_result"`)
121
+ - System-generated (very short single-word messages like "go", "continue")
122
+ - Pure confirmations ("yes", "ok", "sounds good")
123
+
124
+ **KEEP** messages that reveal:
125
+ - Intent ("build a feature that...", "I need X to do Y")
126
+ - Decisions ("let's use approach A instead of B")
127
+ - Architectural choices
128
+ - Problem descriptions
129
+ - Requirements changes
130
+
131
+ ---
132
+
133
+ ### Step 5 — Identify Logical Sessions
134
+
135
+ A "logical session" is a continuous work block. Use these signals to split a day into multiple sessions:
136
+ - Gap of more than 3 hours between messages
137
+ - Different JSONL session IDs with significant time gaps
138
+ - Commits with clearly different themes separated by time
139
+
140
+ If only one logical session exists for a day, create one note. If multiple exist, create one note per session with a suffix: `NNNN - YYYY-MM-DD - Title.md`, `NNNN+1 - YYYY-MM-DD - Title (2).md`.
141
+
142
+ ---
143
+
144
+ ### Step 6 — Generate Note Title
145
+
146
+ The note title comes from synthesizing git commits for that session:
147
+ - If commits share a theme: use that theme ("Implement vault indexer")
148
+ - If commits are varied: use the primary/largest change ("Refactor session notes + misc fixes")
149
+ - Use the conventional commit prefix if present: "feat: Add dark mode" → "Add Dark Mode"
150
+ - Title should be 3-7 words, title-case
151
+
152
+ ---
153
+
154
+ ### Step 7 — Write the Note
155
+
156
+ **Note filename format:** `NNNN - YYYY-MM-DD - Title.md`
157
+
158
+ **Path:** `Notes/YYYY/MM/NNNN - YYYY-MM-DD - Title.md`
159
+
160
+ **Content template:**
161
+
162
+ ```markdown
163
+ # Session: [Title]
164
+
165
+ **Date:** YYYY-MM-DD
166
+ **Status:** Completed
167
+ **Reconstructed:** true (from JSONL + git history)
168
+
169
+ ---
170
+
171
+ ## Work Done
172
+
173
+ [Group related commits into logical sections by theme/component, not by commit order.
174
+ Each section describes WHAT was built and WHY, derived from commit messages and user messages.
175
+ Use present-tense descriptions: "Add X", "Fix Y", "Refactor Z".]
176
+
177
+ ## Key Decisions
178
+
179
+ [Architectural choices, technology selections, approach changes.
180
+ ONLY include decisions that are explicitly stated in user messages or strongly implied by commit message changes (e.g., a series of "revert" commits followed by a new approach).
181
+ Format as bullet points.]
182
+
183
+ ## Known Issues at End of Session
184
+
185
+ [Bugs discovered, things left unfinished.
186
+ Derive from: last commits in session (if they add TODOs or fix-me comments), user messages mentioning problems, or explicit "not done yet" statements.
187
+ Omit this section if nothing can be verified.]
188
+
189
+ ---
190
+
191
+ **Tags:** #[project-slug] #reconstructed
192
+ ```
193
+
194
+ **Filling rules:**
195
+ - Work Done: Always present (use commits as primary source)
196
+ - Key Decisions: Only if verifiable from user messages or commit patterns
197
+ - Known Issues: Only if verifiable; omit section entirely if nothing found
198
+ - Tags: Use the PAI project slug from `pai project detect`
199
+
200
+ ---
201
+
202
+ ### Step 8 — Output Summary
203
+
204
+ After writing (or dry-run preview):
205
+
206
+ ```
207
+ Reconstructed N session notes:
208
+ 0042 - 2026-03-13 - Implement Reranker → Notes/2026/03/
209
+ 0043 - 2026-03-14 - Add Recency Boost → Notes/2026/03/
210
+ 0044 - 2026-03-15 - Zettelkasten Schema Update → Notes/2026/03/
211
+
212
+ Skipped: 2026-03-12 (no commits)
213
+ Skipped: 2026-03-16 (note already exists: 0041 - 2026-03-16 - ...)
214
+ ```
215
+
216
+ If `--commit` was passed:
217
+ ```bash
218
+ git add Notes/
219
+ git commit -m "docs: reconstruct session notes for YYYY-MM-DD to YYYY-MM-DD"
220
+ ```
221
+
222
+ ---
223
+
224
+ ### Anti-Defaults
225
+
226
+ - **NEVER overwrite existing notes.** Skip silently, report in summary.
227
+ - **NEVER invent content.** If a decision isn't in the JSONL or deducible from commits, leave it out.
228
+ - **NEVER fabricate commit messages.** Copy them verbatim from git log.
229
+ - **NEVER assume intent from assistant messages.** Only user messages reveal intent.
230
+ - **NEVER add speculation.** "It appears that..." or "likely..." should not appear in notes.
231
+ - **DO trim noise.** Skip trivial messages ("ok", "yes", "go") that add no signal.
232
+ - **DO handle missing JSONL gracefully.** If no JSONL files found, generate notes from git only — mark the "Key Decisions" section as "Not available (no JSONL transcript found)".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekmidian/pai",
3
- "version": "0.7.0",
3
+ "version": "0.7.2",
4
4
  "description": "PAI Knowledge OS — Personal AI Infrastructure with federated memory and project management",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -7,5 +7,5 @@
7
7
  "required": false,
8
8
  "depends": ["core"],
9
9
  "hooks": "hooks/hooks.json",
10
- "skills": ["Plan", "Review", "Journal", "Research", "Share", "Createskill"]
10
+ "skills": ["Plan", "Review", "Journal", "Research", "Share", "Createskill", "Reconstruct"]
11
11
  }
@@ -0,0 +1,232 @@
1
+ ---
2
+ name: Reconstruct
3
+ description: "Retroactively create session notes from JSONL transcripts and git history when automatic capture failed or was incomplete. USE WHEN user says 'reconstruct sessions', 'rebuild session notes', 'recover session notes', 'retroactively create notes', 'create notes from git history', 'notes are missing', 'backfill session notes', 'reconstruct what we did', OR /reconstruct."
4
+ ---
5
+
6
+ ## Reconstruct Skill
7
+
8
+ USE WHEN user says 'reconstruct sessions', 'rebuild session notes', 'recover session notes', 'retroactively create notes', 'create notes from git history', 'notes are missing', 'backfill session notes', 'reconstruct what we did', OR /reconstruct.
9
+
10
+ ### What This Skill Does
11
+
12
+ Reconstructs session notes for a PAI-registered project by reading:
13
+ 1. Git commit history (authoritative record of what was built)
14
+ 2. JSONL transcripts (user messages reveal intent, decisions, and context)
15
+
16
+ Generates one session note per day (or per logical session if multiple sessions existed in a day), numbered sequentially from the highest existing note number.
17
+
18
+ **ONLY include what can be verified from git log and JSONL. Never invent or embellish content.**
19
+
20
+ ---
21
+
22
+ ### Arguments
23
+
24
+ | Argument | Default | Description |
25
+ |----------|---------|-------------|
26
+ | `--days N` | 7 | Reconstruct notes for the last N days |
27
+ | `--since YYYY-MM-DD` | — | Reconstruct from this date forward |
28
+ | `--until YYYY-MM-DD` | today | Reconstruct up to this date |
29
+ | `--dry-run` | false | Show what would be created without writing files |
30
+ | `--commit` | false | Git commit the created notes after writing |
31
+
32
+ ---
33
+
34
+ ### Pre-Action Check
35
+
36
+ Before starting, verify:
37
+
38
+ 1. **Project is registered**: Run `pai project detect` (or `pai project list`) to confirm the current project is known to PAI and get its Notes directory path.
39
+ 2. **Notes directory exists**: The target directory should be `Notes/YYYY/MM/`. Create it if absent.
40
+ 3. **Find highest existing note number**: Scan all `Notes/**/*.md` files for the pattern `^NNNN` to find max note number. New notes continue from there.
41
+ 4. **NEVER overwrite existing notes**: If a note file already exists for that date/session, skip it and warn the user.
42
+
43
+ ---
44
+
45
+ ### Step 1 — Find JSONL Files
46
+
47
+ Claude Code encodes project paths lossily: `/`, spaces, dots, hyphens all become `-`. A project may have been accessed from multiple base directories (e.g., `~/Daten/Cloud/Development/ai/PAI` and `~/dev/ai/PAI` both encode to different paths).
48
+
49
+ **Search strategy — use glob patterns, not exact paths:**
50
+
51
+ ```bash
52
+ # Find all encoded project directories that might match this project
53
+ ls ~/.claude/projects/ | grep -i "<project-slug-fragment>"
54
+
55
+ # For each candidate, look for JSONL files
56
+ ls ~/.claude/projects/<encoded-path>/*.jsonl 2>/dev/null
57
+
58
+ # Also check the dev copy if the project has one
59
+ # Example: if cwd is ~/dev/ai/PAI, also check ~/Daten/Cloud/Development/ai/PAI encoding
60
+ ```
61
+
62
+ Collect ALL JSONL files from ALL matching encoded paths. A session may have been started from either location.
63
+
64
+ ---
65
+
66
+ ### Step 2 — Determine Time Range
67
+
68
+ Calculate the date range from arguments:
69
+ - `--days 7` → from (today - 7 days) to today
70
+ - `--since 2026-03-01` → from that date to today (or --until date)
71
+ - Default: last 7 days
72
+
73
+ ---
74
+
75
+ ### Step 3 — Extract Git History
76
+
77
+ For each day in the range:
78
+
79
+ ```bash
80
+ git -C <project-dir> log \
81
+ --after="YYYY-MM-DD 00:00:00" \
82
+ --before="YYYY-MM-DD 23:59:59" \
83
+ --format="%H|%ad|%s" \
84
+ --date=short \
85
+ --stat
86
+ ```
87
+
88
+ Group commits by day. If no commits on a day, skip that day (no note to reconstruct).
89
+
90
+ Also capture:
91
+ - Files changed per commit (`--stat`)
92
+ - Author date (not committer date) for accurate day grouping
93
+
94
+ ---
95
+
96
+ ### Step 4 — Extract User Messages from JSONL
97
+
98
+ For each JSONL file, parse messages where `type == "user"` AND `message.role == "user"`:
99
+
100
+ ```
101
+ Each line is a JSON object. Look for:
102
+ {
103
+ "type": "user",
104
+ "message": {
105
+ "role": "user",
106
+ "content": "..." | [{"type": "text", "text": "..."}]
107
+ },
108
+ "timestamp": "2026-03-15T10:23:45.000Z"
109
+ }
110
+ ```
111
+
112
+ Filter messages by timestamp to match the day being reconstructed.
113
+
114
+ Extract:
115
+ - The text content of user messages (ignore tool_result entries)
116
+ - Timestamps for ordering
117
+ - Session ID (`sessionId` field) to group messages per session
118
+
119
+ **SKIP** messages that are:
120
+ - Tool results (`content` is array with `type: "tool_result"`)
121
+ - System-generated (very short single-word messages like "go", "continue")
122
+ - Pure confirmations ("yes", "ok", "sounds good")
123
+
124
+ **KEEP** messages that reveal:
125
+ - Intent ("build a feature that...", "I need X to do Y")
126
+ - Decisions ("let's use approach A instead of B")
127
+ - Architectural choices
128
+ - Problem descriptions
129
+ - Requirements changes
130
+
131
+ ---
132
+
133
+ ### Step 5 — Identify Logical Sessions
134
+
135
+ A "logical session" is a continuous work block. Use these signals to split a day into multiple sessions:
136
+ - Gap of more than 3 hours between messages
137
+ - Different JSONL session IDs with significant time gaps
138
+ - Commits with clearly different themes separated by time
139
+
140
+ If only one logical session exists for a day, create one note. If multiple exist, create one note per session with a suffix: `NNNN - YYYY-MM-DD - Title.md`, `NNNN+1 - YYYY-MM-DD - Title (2).md`.
141
+
142
+ ---
143
+
144
+ ### Step 6 — Generate Note Title
145
+
146
+ The note title comes from synthesizing git commits for that session:
147
+ - If commits share a theme: use that theme ("Implement vault indexer")
148
+ - If commits are varied: use the primary/largest change ("Refactor session notes + misc fixes")
149
+ - Use the conventional commit prefix if present: "feat: Add dark mode" → "Add Dark Mode"
150
+ - Title should be 3-7 words, title-case
151
+
152
+ ---
153
+
154
+ ### Step 7 — Write the Note
155
+
156
+ **Note filename format:** `NNNN - YYYY-MM-DD - Title.md`
157
+
158
+ **Path:** `Notes/YYYY/MM/NNNN - YYYY-MM-DD - Title.md`
159
+
160
+ **Content template:**
161
+
162
+ ```markdown
163
+ # Session: [Title]
164
+
165
+ **Date:** YYYY-MM-DD
166
+ **Status:** Completed
167
+ **Reconstructed:** true (from JSONL + git history)
168
+
169
+ ---
170
+
171
+ ## Work Done
172
+
173
+ [Group related commits into logical sections by theme/component, not by commit order.
174
+ Each section describes WHAT was built and WHY, derived from commit messages and user messages.
175
+ Use present-tense descriptions: "Add X", "Fix Y", "Refactor Z".]
176
+
177
+ ## Key Decisions
178
+
179
+ [Architectural choices, technology selections, approach changes.
180
+ ONLY include decisions that are explicitly stated in user messages or strongly implied by commit message changes (e.g., a series of "revert" commits followed by a new approach).
181
+ Format as bullet points.]
182
+
183
+ ## Known Issues at End of Session
184
+
185
+ [Bugs discovered, things left unfinished.
186
+ Derive from: last commits in session (if they add TODOs or fix-me comments), user messages mentioning problems, or explicit "not done yet" statements.
187
+ Omit this section if nothing can be verified.]
188
+
189
+ ---
190
+
191
+ **Tags:** #[project-slug] #reconstructed
192
+ ```
193
+
194
+ **Filling rules:**
195
+ - Work Done: Always present (use commits as primary source)
196
+ - Key Decisions: Only if verifiable from user messages or commit patterns
197
+ - Known Issues: Only if verifiable; omit section entirely if nothing found
198
+ - Tags: Use the PAI project slug from `pai project detect`
199
+
200
+ ---
201
+
202
+ ### Step 8 — Output Summary
203
+
204
+ After writing (or dry-run preview):
205
+
206
+ ```
207
+ Reconstructed N session notes:
208
+ 0042 - 2026-03-13 - Implement Reranker → Notes/2026/03/
209
+ 0043 - 2026-03-14 - Add Recency Boost → Notes/2026/03/
210
+ 0044 - 2026-03-15 - Zettelkasten Schema Update → Notes/2026/03/
211
+
212
+ Skipped: 2026-03-12 (no commits)
213
+ Skipped: 2026-03-16 (note already exists: 0041 - 2026-03-16 - ...)
214
+ ```
215
+
216
+ If `--commit` was passed:
217
+ ```bash
218
+ git add Notes/
219
+ git commit -m "docs: reconstruct session notes for YYYY-MM-DD to YYYY-MM-DD"
220
+ ```
221
+
222
+ ---
223
+
224
+ ### Anti-Defaults
225
+
226
+ - **NEVER overwrite existing notes.** Skip silently, report in summary.
227
+ - **NEVER invent content.** If a decision isn't in the JSONL or deducible from commits, leave it out.
228
+ - **NEVER fabricate commit messages.** Copy them verbatim from git log.
229
+ - **NEVER assume intent from assistant messages.** Only user messages reveal intent.
230
+ - **NEVER add speculation.** "It appears that..." or "likely..." should not appear in notes.
231
+ - **DO trim noise.** Skip trivial messages ("ok", "yes", "go") that add no signal.
232
+ - **DO handle missing JSONL gracefully.** If no JSONL files found, generate notes from git only — mark the "Key Decisions" section as "Not available (no JSONL transcript found)".
@@ -31,6 +31,7 @@ export {
31
31
  createSessionNote,
32
32
  appendCheckpoint,
33
33
  addWorkToSessionNote,
34
+ isMeaningfulTitle,
34
35
  sanitizeForFilename,
35
36
  extractMeaningfulName,
36
37
  renameSessionNote,
@@ -204,6 +204,14 @@ export function addWorkToSessionNote(notePath: string, workItems: WorkItem[], se
204
204
  console.error(`Added ${workItems.length} work item(s) to: ${basename(notePath)}`);
205
205
  }
206
206
 
207
+ /**
208
+ * Check if a candidate title is meaningless / garbage.
209
+ * Public wrapper around the internal filter for use by other hooks.
210
+ */
211
+ export function isMeaningfulTitle(text: string): boolean {
212
+ return !isMeaninglessCandidate(text);
213
+ }
214
+
207
215
  /** Sanitize a string for use in a filename. */
208
216
  export function sanitizeForFilename(str: string): string {
209
217
  return str
@@ -215,6 +223,35 @@ export function sanitizeForFilename(str: string): string {
215
223
  .substring(0, 50);
216
224
  }
217
225
 
226
+ /**
227
+ * Return true if the candidate string should be rejected as a meaningful name.
228
+ * Rejects file paths, shebangs, timestamps, system noise, XML tags, hashes, etc.
229
+ */
230
+ function isMeaninglessCandidate(text: string): boolean {
231
+ const t = text.trim();
232
+ if (!t) return true;
233
+ if (t.length < 5) return true; // too short to be meaningful
234
+ if (t.startsWith('/') || t.startsWith('~')) return true; // file path
235
+ if (t.startsWith('#!')) return true; // shebang
236
+ if (t.includes('[object Object]')) return true; // serialization artifact
237
+ if (/^\d{4}-\d{2}-\d{2}(T[\d:.Z+-]+)?$/.test(t)) return true; // ISO timestamp
238
+ if (/^\d{1,2}:\d{2}(:\d{2})?(\s*(AM|PM))?$/i.test(t)) return true; // time-only
239
+ if (/^<[a-z-]+[\s/>]/i.test(t)) return true; // XML/HTML tags (<task-notification>, etc.)
240
+ if (/^[0-9a-f]{10,}$/i.test(t)) return true; // hex hash strings
241
+ if (/^Exit code \d+/i.test(t)) return true; // exit code messages
242
+ if (/^Error:/i.test(t)) return true; // error messages
243
+ if (/^This session is being continued/i.test(t)) return true; // continuation boilerplate
244
+ if (/^\(Bash completed/i.test(t)) return true; // bash output noise
245
+ if (/^Task Notification$/i.test(t)) return true; // literal "Task Notification"
246
+ if (/^New Session$/i.test(t)) return true; // placeholder title
247
+ if (/^Recovered Session$/i.test(t)) return true; // placeholder title
248
+ if (/^Continued Session$/i.test(t)) return true; // placeholder title
249
+ if (/^Untitled Session$/i.test(t)) return true; // placeholder title
250
+ if (/^Context Compression$/i.test(t)) return true; // compression artifact
251
+ if (/^[A-Fa-f0-9]{8,}\s+Output$/i.test(t)) return true; // hash + "Output" pattern
252
+ return false;
253
+ }
254
+
218
255
  /**
219
256
  * Extract a meaningful name from session note content and summary.
220
257
  * Looks at Work Done section headers, bold text, and summary.
@@ -228,7 +265,7 @@ export function extractMeaningfulName(noteContent: string, summary: string): str
228
265
  const subheadings = workDoneSection.match(/### ([^\n]+)/g);
229
266
  if (subheadings && subheadings.length > 0) {
230
267
  const firstHeading = subheadings[0].replace('### ', '').trim();
231
- if (firstHeading.length > 5 && firstHeading.length < 60) {
268
+ if (!isMeaninglessCandidate(firstHeading) && firstHeading.length > 5 && firstHeading.length < 60) {
232
269
  return sanitizeForFilename(firstHeading);
233
270
  }
234
271
  }
@@ -236,23 +273,27 @@ export function extractMeaningfulName(noteContent: string, summary: string): str
236
273
  const boldMatches = workDoneSection.match(/\*\*([^*]+)\*\*/g);
237
274
  if (boldMatches && boldMatches.length > 0) {
238
275
  const firstBold = boldMatches[0].replace(/\*\*/g, '').trim();
239
- if (firstBold.length > 3 && firstBold.length < 50) {
276
+ if (!isMeaninglessCandidate(firstBold) && firstBold.length > 3 && firstBold.length < 50) {
240
277
  return sanitizeForFilename(firstBold);
241
278
  }
242
279
  }
243
280
 
244
281
  const numberedItems = workDoneSection.match(/^\d+\.\s+\*\*([^*]+)\*\*/m);
245
- if (numberedItems) return sanitizeForFilename(numberedItems[1]);
282
+ if (numberedItems && !isMeaninglessCandidate(numberedItems[1])) {
283
+ return sanitizeForFilename(numberedItems[1]);
284
+ }
246
285
  }
247
286
 
248
- if (summary && summary.length > 5 && summary !== 'Session completed.') {
287
+ if (summary && summary.length > 5 && summary !== 'Session completed.' && !isMeaninglessCandidate(summary)) {
249
288
  const cleanSummary = summary
250
289
  .replace(/[^\w\s-]/g, ' ')
251
290
  .trim()
252
291
  .split(/\s+/)
253
292
  .slice(0, 5)
254
293
  .join(' ');
255
- if (cleanSummary.length > 3) return sanitizeForFilename(cleanSummary);
294
+ if (cleanSummary.length > 3 && !isMeaninglessCandidate(cleanSummary)) {
295
+ return sanitizeForFilename(cleanSummary);
296
+ }
256
297
  }
257
298
 
258
299
  return '';
@@ -28,6 +28,7 @@ export {
28
28
  createSessionNote,
29
29
  appendCheckpoint,
30
30
  addWorkToSessionNote,
31
+ isMeaningfulTitle,
31
32
  sanitizeForFilename,
32
33
  extractMeaningfulName,
33
34
  renameSessionNote,