@pcircle/memesh 2.8.11 → 2.9.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.
Files changed (79) hide show
  1. package/LICENSE +21 -661
  2. package/README.de.md +171 -0
  3. package/README.es.md +171 -0
  4. package/README.fr.md +171 -0
  5. package/README.id.md +171 -0
  6. package/README.ja.md +171 -0
  7. package/README.ko.md +171 -0
  8. package/README.md +73 -100
  9. package/README.th.md +171 -0
  10. package/README.vi.md +171 -0
  11. package/README.zh-CN.md +171 -0
  12. package/README.zh-TW.md +71 -98
  13. package/dist/knowledge-graph/index.d.ts +22 -1
  14. package/dist/knowledge-graph/index.d.ts.map +1 -1
  15. package/dist/knowledge-graph/index.js +144 -3
  16. package/dist/knowledge-graph/index.js.map +1 -1
  17. package/dist/mcp/ServerInitializer.d.ts.map +1 -1
  18. package/dist/mcp/ServerInitializer.js +1 -1
  19. package/dist/mcp/ServerInitializer.js.map +1 -1
  20. package/dist/mcp/ToolDefinitions.d.ts.map +1 -1
  21. package/dist/mcp/ToolDefinitions.js +47 -55
  22. package/dist/mcp/ToolDefinitions.js.map +1 -1
  23. package/dist/mcp/ToolRouter.d.ts.map +1 -1
  24. package/dist/mcp/ToolRouter.js +4 -4
  25. package/dist/mcp/ToolRouter.js.map +1 -1
  26. package/dist/mcp/daemon/StdioProxyClient.d.ts.map +1 -1
  27. package/dist/mcp/daemon/StdioProxyClient.js +9 -1
  28. package/dist/mcp/daemon/StdioProxyClient.js.map +1 -1
  29. package/dist/mcp/handlers/BuddyHandlers.d.ts +3 -1
  30. package/dist/mcp/handlers/BuddyHandlers.d.ts.map +1 -1
  31. package/dist/mcp/handlers/BuddyHandlers.js +6 -5
  32. package/dist/mcp/handlers/BuddyHandlers.js.map +1 -1
  33. package/dist/mcp/handlers/ToolHandlers.d.ts.map +1 -1
  34. package/dist/mcp/handlers/ToolHandlers.js +1 -2
  35. package/dist/mcp/handlers/ToolHandlers.js.map +1 -1
  36. package/dist/mcp/resources/quick-reference.md +1 -1
  37. package/dist/mcp/schemas/OutputSchemas.d.ts +116 -53
  38. package/dist/mcp/schemas/OutputSchemas.d.ts.map +1 -1
  39. package/dist/mcp/schemas/OutputSchemas.js +64 -26
  40. package/dist/mcp/schemas/OutputSchemas.js.map +1 -1
  41. package/dist/mcp/server-bootstrap.js +89 -9
  42. package/dist/mcp/server-bootstrap.js.map +1 -1
  43. package/dist/mcp/tools/buddy-do.d.ts +2 -1
  44. package/dist/mcp/tools/buddy-do.d.ts.map +1 -1
  45. package/dist/mcp/tools/buddy-do.js +91 -4
  46. package/dist/mcp/tools/buddy-do.js.map +1 -1
  47. package/dist/mcp/tools/buddy-remember.d.ts +0 -5
  48. package/dist/mcp/tools/buddy-remember.d.ts.map +1 -1
  49. package/dist/mcp/tools/buddy-remember.js.map +1 -1
  50. package/dist/mcp/tools/memesh-agent-register.d.ts +20 -0
  51. package/dist/mcp/tools/memesh-agent-register.d.ts.map +1 -0
  52. package/dist/mcp/tools/memesh-agent-register.js +80 -0
  53. package/dist/mcp/tools/memesh-agent-register.js.map +1 -0
  54. package/dist/mcp/tools/memesh-cloud-sync.js +27 -8
  55. package/dist/mcp/tools/memesh-cloud-sync.js.map +1 -1
  56. package/dist/mcp/tools/memesh-metrics.d.ts +13 -0
  57. package/dist/mcp/tools/memesh-metrics.d.ts.map +1 -0
  58. package/dist/mcp/tools/memesh-metrics.js +193 -0
  59. package/dist/mcp/tools/memesh-metrics.js.map +1 -0
  60. package/dist/memory/UnifiedMemoryStore.d.ts +1 -1
  61. package/dist/memory/UnifiedMemoryStore.d.ts.map +1 -1
  62. package/dist/memory/UnifiedMemoryStore.js +4 -3
  63. package/dist/memory/UnifiedMemoryStore.js.map +1 -1
  64. package/package.json +9 -12
  65. package/plugin.json +2 -2
  66. package/scripts/hooks/README.md +230 -0
  67. package/scripts/hooks/__tests__/hook-test-harness.js +218 -0
  68. package/scripts/hooks/__tests__/hooks.test.js +267 -0
  69. package/scripts/hooks/hook-utils.js +899 -0
  70. package/scripts/hooks/post-commit.js +307 -0
  71. package/scripts/hooks/post-tool-use.js +812 -0
  72. package/scripts/hooks/pre-tool-use.js +462 -0
  73. package/scripts/hooks/session-start.js +544 -0
  74. package/scripts/hooks/stop.js +673 -0
  75. package/scripts/hooks/subagent-stop.js +184 -0
  76. package/scripts/hooks/templates/planning-template.md +46 -0
  77. package/scripts/postinstall-lib.js +8 -4
  78. package/scripts/postinstall-new.js +110 -7
  79. package/scripts/skills/comprehensive-code-review/SKILL.md +276 -0
@@ -0,0 +1,544 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * SessionStart Hook - Claude Code Event-Driven Hooks
5
+ *
6
+ * Triggered at the start of each Claude Code session.
7
+ *
8
+ * Features:
9
+ * - Checks MeMesh MCP server availability
10
+ * - Auto-recalls last session key points from MeMesh
11
+ * - Reads recommendations from last session
12
+ * - Displays suggested skills to load
13
+ * - Shows warnings (quota, slow tools, etc.)
14
+ * - Initializes current session state
15
+ */
16
+
17
+ import {
18
+ HOME_DIR,
19
+ STATE_DIR,
20
+ MEMESH_DB_PATH,
21
+ THRESHOLDS,
22
+ readJSONFile,
23
+ writeJSONFile,
24
+ sqliteQueryJSON,
25
+ getTimeAgo,
26
+ logError,
27
+ queryActivePlans,
28
+ renderTimelineCompact,
29
+ } from './hook-utils.js';
30
+ import fs from 'fs';
31
+ import path from 'path';
32
+
33
+ // ============================================================================
34
+ // File Paths
35
+ // ============================================================================
36
+
37
+ const CCB_HEARTBEAT_FILE = path.join(STATE_DIR, 'ccb-heartbeat.json');
38
+ const MCP_SETTINGS_FILE = path.join(HOME_DIR, '.claude', 'mcp_settings.json');
39
+ const RECOMMENDATIONS_FILE = path.join(STATE_DIR, 'recommendations.json');
40
+ const SESSION_CONTEXT_FILE = path.join(STATE_DIR, 'session-context.json');
41
+ const CURRENT_SESSION_FILE = path.join(STATE_DIR, 'current-session.json');
42
+ const LAST_SESSION_CACHE_FILE = path.join(STATE_DIR, 'last-session-summary.json');
43
+
44
+ /** Maximum cache age: 7 days */
45
+ const CACHE_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000;
46
+
47
+ // ============================================================================
48
+ // MeMesh Status Check
49
+ // ============================================================================
50
+
51
+ /**
52
+ * Check MeMesh MCP Server availability
53
+ * @returns {{ configured: boolean, running: boolean, lastHeartbeat: string|null, serverPath: string|null }}
54
+ */
55
+ function checkCCBAvailability() {
56
+ const result = {
57
+ configured: false,
58
+ running: false,
59
+ lastHeartbeat: null,
60
+ serverPath: null,
61
+ };
62
+
63
+ // Check if MeMesh is configured in MCP settings
64
+ try {
65
+ if (fs.existsSync(MCP_SETTINGS_FILE)) {
66
+ const mcpSettings = JSON.parse(fs.readFileSync(MCP_SETTINGS_FILE, 'utf-8'));
67
+
68
+ // Check for MeMesh and legacy names (backward compatibility)
69
+ const ccbNames = [
70
+ 'memesh',
71
+ '@pcircle/memesh',
72
+ '@pcircle/claude-code-buddy-mcp',
73
+ 'claude-code-buddy',
74
+ 'ccb',
75
+ ];
76
+
77
+ for (const name of ccbNames) {
78
+ if (mcpSettings.mcpServers && mcpSettings.mcpServers[name]) {
79
+ result.configured = true;
80
+ result.serverPath = mcpSettings.mcpServers[name].args?.[0] || 'configured';
81
+ break;
82
+ }
83
+ }
84
+ }
85
+ } catch {
86
+ // Ignore parse errors
87
+ }
88
+
89
+ // Check heartbeat file (MeMesh writes this when running)
90
+ try {
91
+ if (fs.existsSync(CCB_HEARTBEAT_FILE)) {
92
+ const heartbeat = JSON.parse(fs.readFileSync(CCB_HEARTBEAT_FILE, 'utf-8'));
93
+ result.lastHeartbeat = heartbeat.timestamp;
94
+
95
+ const heartbeatTime = new Date(heartbeat.timestamp).getTime();
96
+ const now = Date.now();
97
+
98
+ if (now - heartbeatTime < THRESHOLDS.HEARTBEAT_VALIDITY) {
99
+ result.running = true;
100
+ }
101
+ }
102
+ } catch {
103
+ // Ignore errors
104
+ }
105
+
106
+ return result;
107
+ }
108
+
109
+ /**
110
+ * Display MeMesh status and reminder
111
+ */
112
+ function displayCCBStatus(ccbStatus) {
113
+ console.log('═'.repeat(60));
114
+ console.log(' šŸ¤– MeMesh Status');
115
+ console.log('═'.repeat(60));
116
+
117
+ if (!ccbStatus.configured) {
118
+ console.log('');
119
+ console.log(' āš ļø MeMesh MCP Server is NOT configured!');
120
+ console.log('');
121
+ console.log(' MeMesh provides memory management and knowledge graph tools.');
122
+ console.log(' To configure MeMesh, add it to ~/.claude/mcp_settings.json');
123
+ console.log('');
124
+ console.log(' Available MeMesh tools when connected:');
125
+ console.log(' • buddy-remember: Query past knowledge');
126
+ console.log(' • buddy-do: Execute common operations');
127
+ console.log(' • memesh-create-entities: Store new knowledge to graph');
128
+ console.log('');
129
+ } else if (!ccbStatus.running) {
130
+ console.log('');
131
+ console.log(' ā„¹ļø MeMesh is configured but status unknown');
132
+ console.log(` Path: ${ccbStatus.serverPath}`);
133
+ console.log('');
134
+ console.log(' šŸ“ REMINDER: Use MeMesh tools for memory management:');
135
+ console.log('');
136
+ console.log(' Before starting work:');
137
+ console.log(' buddy-remember "relevant topic" - Query past experiences');
138
+ console.log('');
139
+ console.log(' After completing work:');
140
+ console.log(' memesh-create-entities - Store new learnings');
141
+ console.log(' memesh-record-mistake - Record errors for future reference');
142
+ console.log('');
143
+ console.log(' šŸ’” If MeMesh tools fail, check MCP server status.');
144
+ console.log('');
145
+ } else {
146
+ console.log('');
147
+ console.log(' āœ… MeMesh MCP Server is running');
148
+ console.log(` Last heartbeat: ${ccbStatus.lastHeartbeat}`);
149
+ console.log('');
150
+ console.log(' šŸ“‹ Session Start Checklist:');
151
+ console.log(' ☐ buddy-remember - Query relevant past knowledge');
152
+ console.log('');
153
+ console.log(' šŸ“‹ Session End Checklist:');
154
+ console.log(' ☐ memesh-create-entities - Store new learnings');
155
+ console.log(' ☐ memesh-record-mistake - Record any errors');
156
+ console.log('');
157
+ }
158
+
159
+ console.log('═'.repeat(60));
160
+ console.log('');
161
+ }
162
+
163
+ // ============================================================================
164
+ // Memory Recall
165
+ // ============================================================================
166
+
167
+ /**
168
+ * Try to read session summary from cache file (fast path).
169
+ * Cache is written by stop.js on session end.
170
+ * @returns {{ entityName: string, createdAt: string, metadata: object, keyPoints: string[] } | null}
171
+ */
172
+ function recallFromCache() {
173
+ try {
174
+ if (!fs.existsSync(LAST_SESSION_CACHE_FILE)) {
175
+ return null;
176
+ }
177
+
178
+ const cache = readJSONFile(LAST_SESSION_CACHE_FILE, null);
179
+ if (!cache || !cache.savedAt || !cache.keyPoints) {
180
+ return null;
181
+ }
182
+
183
+ // Check staleness
184
+ const cacheAge = Date.now() - new Date(cache.savedAt).getTime();
185
+ if (cacheAge > CACHE_MAX_AGE_MS) {
186
+ // Stale cache — delete it
187
+ try { fs.unlinkSync(LAST_SESSION_CACHE_FILE); } catch { /* ignore */ }
188
+ return null;
189
+ }
190
+
191
+ return {
192
+ entityName: 'session_cache',
193
+ createdAt: cache.savedAt,
194
+ metadata: {
195
+ duration: cache.duration,
196
+ toolCount: cache.toolCount,
197
+ },
198
+ keyPoints: cache.keyPoints,
199
+ };
200
+ } catch (error) {
201
+ logError('recallFromCache', error);
202
+ return null;
203
+ }
204
+ }
205
+
206
+ /**
207
+ * Recall recent session key points from MeMesh (slow path — SQLite query).
208
+ * Used as fallback when cache is not available.
209
+ * @returns {{ entityName: string, createdAt: string, metadata: object, keyPoints: string[] } | null}
210
+ */
211
+ function recallFromSQLite() {
212
+ try {
213
+ if (!fs.existsSync(MEMESH_DB_PATH)) {
214
+ return null;
215
+ }
216
+
217
+ const cutoffDate = new Date();
218
+ cutoffDate.setDate(cutoffDate.getDate() - THRESHOLDS.RECALL_DAYS);
219
+ const cutoffISO = cutoffDate.toISOString();
220
+
221
+ const query = `
222
+ SELECT id, name, metadata, created_at
223
+ FROM entities
224
+ WHERE type = ? AND created_at > ?
225
+ ORDER BY created_at DESC
226
+ LIMIT 1
227
+ `.replace(/\n/g, ' ');
228
+
229
+ // Use JSON mode to avoid pipe-split issues with | in metadata
230
+ const entityRows = sqliteQueryJSON(
231
+ MEMESH_DB_PATH,
232
+ query,
233
+ ['session_keypoint', cutoffISO]
234
+ );
235
+
236
+ if (!entityRows || entityRows.length === 0) {
237
+ return null;
238
+ }
239
+
240
+ const row = entityRows[0];
241
+ const entityId = row.id;
242
+ const entityName = row.name;
243
+ const createdAt = row.created_at;
244
+
245
+ // Observations also use JSON mode for safety
246
+ const obsRows = sqliteQueryJSON(
247
+ MEMESH_DB_PATH,
248
+ 'SELECT content FROM observations WHERE entity_id = ? ORDER BY created_at ASC',
249
+ [entityId]
250
+ );
251
+
252
+ const keyPoints = (obsRows || []).map(r => r.content).filter(Boolean);
253
+
254
+ let parsedMetadata = {};
255
+ try {
256
+ parsedMetadata = JSON.parse(row.metadata || '{}');
257
+ } catch {
258
+ // Ignore parse errors
259
+ }
260
+
261
+ return {
262
+ entityName,
263
+ createdAt,
264
+ metadata: parsedMetadata,
265
+ keyPoints,
266
+ };
267
+ } catch (error) {
268
+ logError('recallFromSQLite', error);
269
+ return null;
270
+ }
271
+ }
272
+
273
+ /**
274
+ * Recall recent session key points — cache-first, SQLite fallback.
275
+ * @returns {{ entityName: string, createdAt: string, metadata: object, keyPoints: string[] } | null}
276
+ */
277
+ function recallRecentKeyPoints() {
278
+ // Fast path: read from cache file (no sqlite3 spawn)
279
+ const cached = recallFromCache();
280
+ if (cached) {
281
+ return cached;
282
+ }
283
+
284
+ // Slow path: query SQLite
285
+ return recallFromSQLite();
286
+ }
287
+
288
+ /**
289
+ * Display recalled key points from last session
290
+ */
291
+ function displayRecalledMemory(recalledData) {
292
+ console.log('═'.repeat(60));
293
+ console.log(' 🧠 MeMesh Memory Recall');
294
+ console.log('═'.repeat(60));
295
+
296
+ if (!recalledData || !recalledData.keyPoints || recalledData.keyPoints.length === 0) {
297
+ console.log('');
298
+ console.log(' ā„¹ļø No recent memories found (last 30 days)');
299
+ console.log(' šŸ’” Memories will be auto-saved when this session ends');
300
+ console.log('');
301
+ console.log('═'.repeat(60));
302
+ console.log('');
303
+ return;
304
+ }
305
+
306
+ console.log('');
307
+
308
+ // Display timestamp
309
+ const savedTime = new Date(recalledData.createdAt);
310
+ const timeAgo = getTimeAgo(savedTime);
311
+ console.log(` šŸ• Saved: ${timeAgo}`);
312
+
313
+ // Display metadata if available
314
+ if (recalledData.metadata) {
315
+ const meta = recalledData.metadata;
316
+ if (meta.duration) {
317
+ console.log(` ā±ļø Last session duration: ${meta.duration}`);
318
+ }
319
+ if (meta.toolCount) {
320
+ console.log(` šŸ› ļø Tools used: ${meta.toolCount}`);
321
+ }
322
+ }
323
+
324
+ console.log('');
325
+ console.log(' šŸ“‹ Key Points:');
326
+
327
+ // Display key points with formatting
328
+ recalledData.keyPoints.forEach(point => {
329
+ if (point.startsWith('[SESSION]')) {
330
+ console.log(` šŸ“Š ${point.replace('[SESSION] ', '')}`);
331
+ } else if (point.startsWith('[WORK]')) {
332
+ console.log(` šŸ“ ${point.replace('[WORK] ', '')}`);
333
+ } else if (point.startsWith('[COMMIT]')) {
334
+ console.log(` āœ… ${point.replace('[COMMIT] ', '')}`);
335
+ } else if (point.startsWith('[ISSUE]') || point.startsWith('[PROBLEM]')) {
336
+ console.log(` āš ļø ${point.replace(/\[(ISSUE|PROBLEM)\] /, '')}`);
337
+ } else if (point.startsWith('[LEARN]')) {
338
+ console.log(` šŸ’” ${point.replace('[LEARN] ', '')}`);
339
+ } else if (point.startsWith('[TASK]')) {
340
+ console.log(` šŸ“ ${point.replace('[TASK] ', '')}`);
341
+ } else if (point.startsWith('[DECISION]')) {
342
+ console.log(` šŸŽÆ ${point.replace('[DECISION] ', '')}`);
343
+ } else if (point.startsWith('[PATTERN]')) {
344
+ console.log(` šŸ”„ ${point.replace('[PATTERN] ', '')}`);
345
+ } else if (point.startsWith('[SCOPE]') || point.startsWith('[FOCUS]')) {
346
+ console.log(` šŸŽÆ ${point.replace(/\[(SCOPE|FOCUS)\] /, '')}`);
347
+ } else if (point.startsWith('[NOTE]')) {
348
+ console.log(` šŸ“Œ ${point.replace('[NOTE] ', '')}`);
349
+ } else {
350
+ console.log(` • ${point}`);
351
+ }
352
+ });
353
+
354
+ console.log('');
355
+ console.log('═'.repeat(60));
356
+ console.log('');
357
+ }
358
+
359
+ // ============================================================================
360
+ // CLAUDE.md Reload
361
+ // ============================================================================
362
+
363
+ /**
364
+ * Find and display project CLAUDE.md content on session start.
365
+ * This ensures instructions are fresh in context even after compaction.
366
+ * Searches: CWD/.claude/CLAUDE.md, CWD/CLAUDE.md
367
+ */
368
+ function reloadClaudeMd() {
369
+ const cwd = process.cwd();
370
+ const candidates = [
371
+ path.join(cwd, '.claude', 'CLAUDE.md'),
372
+ path.join(cwd, 'CLAUDE.md'),
373
+ ];
374
+
375
+ for (const candidate of candidates) {
376
+ try {
377
+ if (fs.existsSync(candidate)) {
378
+ const content = fs.readFileSync(candidate, 'utf-8');
379
+ const lineCount = content.split('\n').length;
380
+ const relativePath = path.relative(cwd, candidate);
381
+
382
+ console.log('═'.repeat(60));
383
+ console.log(' šŸ“‹ CLAUDE.md Reloaded');
384
+ console.log('═'.repeat(60));
385
+ console.log('');
386
+ console.log(` Source: ${relativePath} (${lineCount} lines)`);
387
+ console.log('');
388
+ console.log(content);
389
+ console.log('');
390
+ console.log('═'.repeat(60));
391
+ console.log('');
392
+ return;
393
+ }
394
+ } catch {
395
+ // Skip unreadable files
396
+ }
397
+ }
398
+ }
399
+
400
+ // ============================================================================
401
+ // Main Session Start Logic
402
+ // ============================================================================
403
+
404
+ // ============================================================================
405
+ // Active Plans Display (Beta)
406
+ // ============================================================================
407
+
408
+ /**
409
+ * Display active plans with compact timeline.
410
+ * Also warns about stale plans (no progress for 7+ days).
411
+ */
412
+ function displayActivePlans() {
413
+ try {
414
+ if (!fs.existsSync(MEMESH_DB_PATH)) return;
415
+
416
+ const activePlans = queryActivePlans(MEMESH_DB_PATH);
417
+ if (activePlans.length === 0) return;
418
+
419
+ console.log('═'.repeat(60));
420
+ console.log(' šŸ“‹ Active Plans');
421
+ console.log('═'.repeat(60));
422
+ console.log('');
423
+
424
+ for (const plan of activePlans) {
425
+ console.log(renderTimelineCompact(plan));
426
+
427
+ // Stale plan warning (7+ days since last progress)
428
+ const lastStep = plan.metadata.stepsDetail
429
+ ?.filter(s => s.completed && s.date)
430
+ .sort((a, b) => b.date.localeCompare(a.date))[0];
431
+ if (lastStep && lastStep.date) {
432
+ const daysSince = Math.floor((Date.now() - new Date(lastStep.date).getTime()) / (1000 * 60 * 60 * 24));
433
+ if (daysSince >= 7) {
434
+ console.log(` āš ļø No progress for ${daysSince} days`);
435
+ }
436
+ }
437
+ console.log('');
438
+ }
439
+
440
+ console.log('═'.repeat(60));
441
+ console.log('');
442
+ } catch (error) {
443
+ logError('displayActivePlans', error);
444
+ }
445
+ }
446
+
447
+ function sessionStart() {
448
+ console.log('\nšŸš€ Smart-Agents Session Started\n');
449
+
450
+ // Reload project CLAUDE.md into context
451
+ reloadClaudeMd();
452
+
453
+ // Check MeMesh availability
454
+ const ccbStatus = checkCCBAvailability();
455
+ displayCCBStatus(ccbStatus);
456
+
457
+ // Auto-recall last session's key points from MeMesh
458
+ const recalledMemory = recallRecentKeyPoints();
459
+ displayRecalledMemory(recalledMemory);
460
+
461
+ // Display active plans (beta)
462
+ displayActivePlans();
463
+
464
+ // Read recommendations from last session
465
+ const recommendations = readJSONFile(RECOMMENDATIONS_FILE, {
466
+ recommendedSkills: [],
467
+ detectedPatterns: [],
468
+ warnings: [],
469
+ lastUpdated: null,
470
+ });
471
+
472
+ // Read session context
473
+ const sessionContext = readJSONFile(SESSION_CONTEXT_FILE, {
474
+ tokenQuota: { used: 0, limit: 200000 },
475
+ learnedPatterns: [],
476
+ lastSessionDate: null,
477
+ });
478
+
479
+ // Display recommendations
480
+ if (recommendations.recommendedSkills?.length > 0) {
481
+ console.log('šŸ“š Recommended skills based on last session:');
482
+ recommendations.recommendedSkills.forEach(skill => {
483
+ const priority = skill.priority === 'high' ? 'šŸ”“' : skill.priority === 'medium' ? '🟔' : '🟢';
484
+ console.log(` ${priority} ${skill.name} - ${skill.reason}`);
485
+ });
486
+ console.log('');
487
+ }
488
+
489
+ // Display detected patterns
490
+ if (recommendations.detectedPatterns?.length > 0) {
491
+ console.log('✨ Detected patterns:');
492
+ recommendations.detectedPatterns.slice(0, 3).forEach(pattern => {
493
+ console.log(` • ${pattern.description}`);
494
+ if (pattern.suggestion) {
495
+ console.log(` šŸ’” ${pattern.suggestion}`);
496
+ }
497
+ });
498
+ console.log('');
499
+ }
500
+
501
+ // Display warnings
502
+ if (recommendations.warnings?.length > 0) {
503
+ console.log('āš ļø Warnings:');
504
+ recommendations.warnings.forEach(warning => {
505
+ console.log(` • ${warning}`);
506
+ });
507
+ console.log('');
508
+ }
509
+
510
+ // Display quota info (guard against division by zero)
511
+ const quotaLimit = sessionContext.tokenQuota?.limit || 1;
512
+ const quotaUsed = sessionContext.tokenQuota?.used || 0;
513
+ const quotaPercentNum = (quotaUsed / quotaLimit) * 100;
514
+ if (quotaPercentNum > 80) {
515
+ console.log(`šŸ”“ Quota usage: ${quotaPercentNum.toFixed(1)}% (please monitor usage)\n`);
516
+ } else if (quotaPercentNum > 50) {
517
+ console.log(`🟔 Quota usage: ${quotaPercentNum.toFixed(1)}%\n`);
518
+ }
519
+
520
+ // Initialize current session
521
+ const currentSession = {
522
+ startTime: new Date().toISOString(),
523
+ toolCalls: [],
524
+ patterns: [],
525
+ ccbStatus: ccbStatus,
526
+ };
527
+
528
+ if (writeJSONFile(CURRENT_SESSION_FILE, currentSession)) {
529
+ console.log('āœ… Session initialized, ready to work!\n');
530
+ } else {
531
+ console.log('āš ļø Session initialization failed, but you can continue working\n');
532
+ }
533
+ }
534
+
535
+ // ============================================================================
536
+ // Execute
537
+ // ============================================================================
538
+
539
+ try {
540
+ sessionStart();
541
+ } catch (error) {
542
+ console.error('āŒ SessionStart hook error:', error.message);
543
+ process.exit(0); // Never block Claude Code on hook errors
544
+ }