sumulige-claude 1.0.11 → 1.1.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/lib/utils.js CHANGED
@@ -8,20 +8,35 @@ const fs = require('fs');
8
8
  const path = require('path');
9
9
 
10
10
  /**
11
- * Recursively copy directory contents
11
+ * Copy mode for template deployment
12
+ */
13
+ const CopyMode = {
14
+ SAFE: 'safe', // Skip existing files (no overwrite, no backup)
15
+ BACKUP: 'backup', // Backup then overwrite (default)
16
+ FORCE: 'force' // Overwrite without backup
17
+ };
18
+
19
+ /**
20
+ * Recursively copy directory contents with backup support
12
21
  * @param {string} src - Source directory
13
22
  * @param {string} dest - Destination directory
14
- * @param {boolean} overwrite - Whether to overwrite existing files
15
- * @returns {number} Number of files copied
23
+ * @param {boolean|string} mode - Copy mode: true/false (legacy) or CopyMode enum
24
+ * @param {string} backupDir - Backup directory path
25
+ * @returns {object} Copy result { copied, skipped, backedup }
16
26
  */
17
- exports.copyRecursive = function(src, dest, overwrite = false) {
18
- if (!fs.existsSync(src)) return 0;
27
+ exports.copyRecursive = function(src, dest, mode = CopyMode.BACKUP, backupDir = null) {
28
+ if (!fs.existsSync(src)) return { copied: 0, skipped: 0, backedup: 0 };
29
+
30
+ // Legacy support: convert boolean to CopyMode
31
+ if (typeof mode === 'boolean') {
32
+ mode = mode ? CopyMode.FORCE : CopyMode.SAFE;
33
+ }
19
34
 
20
35
  if (!fs.existsSync(dest)) {
21
36
  fs.mkdirSync(dest, { recursive: true });
22
37
  }
23
38
 
24
- let count = 0;
39
+ const result = { copied: 0, skipped: 0, backedup: 0 };
25
40
  const entries = fs.readdirSync(src, { withFileTypes: true });
26
41
 
27
42
  for (const entry of entries) {
@@ -29,19 +44,89 @@ exports.copyRecursive = function(src, dest, overwrite = false) {
29
44
  const destPath = path.join(dest, entry.name);
30
45
 
31
46
  if (entry.isDirectory()) {
32
- count += exports.copyRecursive(srcPath, destPath, overwrite);
33
- } else if (overwrite || !fs.existsSync(destPath)) {
34
- fs.copyFileSync(srcPath, destPath);
35
- // Add execute permission for scripts
36
- if (entry.name.endsWith('.sh') || entry.name.endsWith('.cjs')) {
37
- fs.chmodSync(destPath, 0o755);
47
+ const subResult = exports.copyRecursive(
48
+ srcPath,
49
+ destPath,
50
+ mode,
51
+ backupDir ? path.join(backupDir, entry.name) : null
52
+ );
53
+ result.copied += subResult.copied;
54
+ result.skipped += subResult.skipped;
55
+ result.backedup += subResult.backedup;
56
+ } else {
57
+ const action = copyFile(srcPath, destPath, mode, backupDir);
58
+ result[action.type === 'copied' ? 'copied' : action.type]++;
59
+ if (action.type === 'backedup') {
60
+ result.copied++;
61
+ result.backedup++;
38
62
  }
39
- count++;
40
63
  }
41
64
  }
42
- return count;
65
+ return result;
43
66
  };
44
67
 
68
+ /**
69
+ * Copy a single file with backup support
70
+ * @param {string} srcPath - Source file path
71
+ * @param {string} destPath - Destination file path
72
+ * @param {string} mode - Copy mode
73
+ * @param {string} backupDir - Backup directory path
74
+ * @returns {object} Action result { type, backupPath }
75
+ */
76
+ function copyFile(srcPath, destPath, mode, backupDir) {
77
+ // File doesn't exist - just copy
78
+ if (!fs.existsSync(destPath)) {
79
+ fs.copyFileSync(srcPath, destPath);
80
+ setExecutablePermission(destPath);
81
+ return { type: 'copied' };
82
+ }
83
+
84
+ // File exists - handle based on mode
85
+ switch (mode) {
86
+ case CopyMode.SAFE:
87
+ // Skip existing files
88
+ return { type: 'skipped' };
89
+
90
+ case CopyMode.FORCE:
91
+ // Overwrite without backup
92
+ fs.copyFileSync(srcPath, destPath);
93
+ setExecutablePermission(destPath);
94
+ return { type: 'copied' };
95
+
96
+ case CopyMode.BACKUP:
97
+ default:
98
+ // Backup then overwrite
99
+ if (backupDir) {
100
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0];
101
+ const backupFileName = path.basename(destPath) + '.' + timestamp + '.bak';
102
+ const backupPath = path.join(backupDir, backupFileName);
103
+
104
+ fs.mkdirSync(backupDir, { recursive: true });
105
+ fs.copyFileSync(destPath, backupPath);
106
+ setExecutablePermission(backupPath);
107
+
108
+ fs.copyFileSync(srcPath, destPath);
109
+ setExecutablePermission(destPath);
110
+ return { type: 'backedup', backupPath };
111
+ } else {
112
+ // No backup dir, just overwrite
113
+ fs.copyFileSync(srcPath, destPath);
114
+ setExecutablePermission(destPath);
115
+ return { type: 'copied' };
116
+ }
117
+ }
118
+ }
119
+
120
+ /**
121
+ * Set executable permission for script files
122
+ * @param {string} filePath - File path
123
+ */
124
+ function setExecutablePermission(filePath) {
125
+ if (filePath.endsWith('.sh') || filePath.endsWith('.cjs')) {
126
+ fs.chmodSync(filePath, 0o755);
127
+ }
128
+ }
129
+
45
130
  /**
46
131
  * Ensure a directory exists
47
132
  * @param {string} dir - Directory path
@@ -60,3 +145,6 @@ exports.ensureDir = function(dir) {
60
145
  exports.toTitleCase = function(str) {
61
146
  return str.replace(/\b\w/g, char => char.toUpperCase());
62
147
  };
148
+
149
+ // Export CopyMode for use in other modules
150
+ exports.CopyMode = CopyMode;
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Version Check - Check for updates from npm registry
3
+ *
4
+ * Implements lazy checking with local caching to minimize
5
+ * network requests and performance impact.
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+ const https = require('https');
11
+
12
+ const CONFIG_DIR = path.join(process.env.HOME, '.claude');
13
+ const CHECK_FILE = path.join(CONFIG_DIR, '.last-update-check');
14
+ const ONE_DAY = 24 * 60 * 60 * 1000;
15
+
16
+ // Current version from package.json
17
+ const CURRENT_VERSION = require('../package.json').version;
18
+ const PACKAGE_NAME = 'sumulige-claude';
19
+
20
+ /**
21
+ * Get timestamp of last update check
22
+ * @returns {number} Timestamp of last check, or 0 if never checked
23
+ */
24
+ function getLastCheckTime() {
25
+ try {
26
+ const content = fs.readFileSync(CHECK_FILE, 'utf-8');
27
+ return parseInt(content, 10) || 0;
28
+ } catch {
29
+ return 0;
30
+ }
31
+ }
32
+
33
+ /**
34
+ * Save timestamp of current update check
35
+ * @param {number} timestamp - Timestamp to save
36
+ */
37
+ function saveLastCheckTime(timestamp = Date.now()) {
38
+ try {
39
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
40
+ fs.writeFileSync(CHECK_FILE, timestamp.toString());
41
+ } catch {
42
+ // Ignore errors
43
+ }
44
+ }
45
+
46
+ /**
47
+ * Fetch latest version from npm registry
48
+ * @returns {Promise<string|null>} Latest version or null if failed
49
+ */
50
+ function fetchLatestVersion() {
51
+ return new Promise((resolve) => {
52
+ const options = {
53
+ hostname: 'registry.npmjs.org',
54
+ path: `/${PACKAGE_NAME}`,
55
+ timeout: 5000, // 5 second timeout
56
+ headers: {
57
+ 'User-Agent': `${PACKAGE_NAME}/${CURRENT_VERSION}`
58
+ }
59
+ };
60
+
61
+ const req = https.get(options, (res) => {
62
+ let data = '';
63
+ res.on('data', chunk => data += chunk);
64
+ res.on('end', () => {
65
+ try {
66
+ const pkg = JSON.parse(data);
67
+ resolve(pkg['dist-tags']?.latest || null);
68
+ } catch {
69
+ resolve(null);
70
+ }
71
+ });
72
+ });
73
+
74
+ req.on('error', () => resolve(null));
75
+ req.on('timeout', () => {
76
+ req.destroy();
77
+ resolve(null);
78
+ });
79
+ req.setTimeout(5000);
80
+ });
81
+ }
82
+
83
+ /**
84
+ * Check for updates (with caching)
85
+ * @param {Object} options - Check options
86
+ * @param {boolean} options.force - Force check even if within cache period
87
+ * @param {boolean} options.silent - Don't print messages
88
+ * @returns {Promise<Object>} Check result { current, latest, updateAvailable }
89
+ */
90
+ async function checkUpdate(options = {}) {
91
+ const { force = false, silent = false } = options;
92
+ const now = Date.now();
93
+ const lastCheck = getLastCheckTime();
94
+ const shouldCheck = force || (now - lastCheck > ONE_DAY);
95
+
96
+ if (!shouldCheck) {
97
+ return {
98
+ current: CURRENT_VERSION,
99
+ latest: null,
100
+ updateAvailable: false,
101
+ cached: true
102
+ };
103
+ }
104
+
105
+ const latest = await fetchLatestVersion();
106
+ saveLastCheckTime(now);
107
+
108
+ // Only show update if remote version is NEWER than current
109
+ const updateAvailable = latest && compareVersions(latest, CURRENT_VERSION) > 0;
110
+ const result = {
111
+ current: CURRENT_VERSION,
112
+ latest,
113
+ updateAvailable,
114
+ cached: false
115
+ };
116
+
117
+ if (!silent && updateAvailable) {
118
+ console.log('');
119
+ console.log(`💡 新版本 v${latest} 可用 (当前: v${CURRENT_VERSION})`);
120
+ console.log(` 运行: npm update -g ${PACKAGE_NAME}`);
121
+ console.log('');
122
+ }
123
+
124
+ return result;
125
+ }
126
+
127
+ /**
128
+ * Get current version
129
+ * @returns {string} Current version
130
+ */
131
+ function getCurrentVersion() {
132
+ return CURRENT_VERSION;
133
+ }
134
+
135
+ /**
136
+ * Parse version string to comparable array
137
+ * @param {string} version - Version string (e.g., "1.2.3")
138
+ * @returns {number[]} Comparable array [major, minor, patch]
139
+ */
140
+ function parseVersion(version) {
141
+ return version.split('.').map(Number).filter(n => !isNaN(n));
142
+ }
143
+
144
+ /**
145
+ * Compare two versions
146
+ * @param {string} v1 - First version
147
+ * @param {string} v2 - Second version
148
+ * @returns {number} -1 if v1 < v2, 0 if equal, 1 if v1 > v2
149
+ */
150
+ function compareVersions(v1, v2) {
151
+ const parts1 = parseVersion(v1);
152
+ const parts2 = parseVersion(v2);
153
+ const maxLen = Math.max(parts1.length, parts2.length);
154
+
155
+ for (let i = 0; i < maxLen; i++) {
156
+ const p1 = parts1[i] || 0;
157
+ const p2 = parts2[i] || 0;
158
+ if (p1 < p2) return -1;
159
+ if (p1 > p2) return 1;
160
+ }
161
+ return 0;
162
+ }
163
+
164
+ exports.checkUpdate = checkUpdate;
165
+ exports.getCurrentVersion = getCurrentVersion;
166
+ exports.compareVersions = compareVersions;
167
+ exports.getLastCheckTime = getLastCheckTime;
168
+ exports.saveLastCheckTime = saveLastCheckTime;
169
+ exports.CURRENT_VERSION = CURRENT_VERSION;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sumulige-claude",
3
- "version": "1.0.11",
3
+ "version": "1.1.0",
4
4
  "description": "The Best Agent Harness for Claude Code",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -14,7 +14,11 @@
14
14
  "postinstall": "node cli.js init",
15
15
  "sync": "node scripts/sync-external.mjs",
16
16
  "update-registry": "node scripts/update-registry.mjs",
17
- "sync:all": "npm run sync && npm run update-registry"
17
+ "sync:all": "npm run sync && npm run update-registry",
18
+ "release": "standard-version",
19
+ "release:patch": "standard-version --release-as patch",
20
+ "release:minor": "standard-version --release-as minor",
21
+ "release:major": "standard-version --release-as major"
18
22
  },
19
23
  "keywords": [
20
24
  "claude",
@@ -41,6 +45,7 @@
41
45
  "mock-fs": "^5.5.0",
42
46
  "prettier": "^3.7.4",
43
47
  "sinon": "^21.0.1",
48
+ "standard-version": "^9.5.0",
44
49
  "yaml": "^2.8.2"
45
50
  }
46
51
  }
@@ -9,10 +9,12 @@
9
9
  * 功能:
10
10
  * - 检查项目是否已启动
11
11
  * - 如果未启动,提示 AI 进行项目规划
12
+ * - 生成规划文档后,自动创建任务到 backlog/
12
13
  */
13
14
 
14
15
  const fs = require('fs');
15
16
  const path = require('path');
17
+ const { execSync } = require('child_process');
16
18
 
17
19
  const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
18
20
  const TEMPLATES_DIR = path.join(PROJECT_DIR, '.claude/templates');
@@ -20,6 +22,15 @@ const KICKOFF_FILE = path.join(PROJECT_DIR, 'PROJECT_KICKOFF.md');
20
22
  const PLAN_FILE = path.join(PROJECT_DIR, 'TASK_PLAN.md');
21
23
  const PROPOSAL_FILE = path.join(PROJECT_DIR, 'PROJECT_PROPOSAL.md');
22
24
  const HINT_FILE = path.join(PROJECT_DIR, '.claude/.kickoff-hint.txt');
25
+ const TODOS_DIR = path.join(PROJECT_DIR, 'development/todos');
26
+ const BACKLOG_DIR = path.join(TODOS_DIR, 'backlog');
27
+
28
+ // 任务类型图标
29
+ const TASK_TYPES = {
30
+ research: { icon: '📊', dir: 'research', name: 'Research' },
31
+ develop: { icon: '💻', dir: 'develop', name: 'Develop' },
32
+ test: { icon: '🧪', dir: 'test', name: 'Test' }
33
+ };
23
34
 
24
35
  // 检查项目是否已启动
25
36
  function isProjectStarted() {
@@ -35,6 +46,169 @@ function isForceKickoff() {
35
46
  toolInput.includes('重新规划');
36
47
  }
37
48
 
49
+ // 检查任务规划文档是否存在
50
+ function hasTaskPlan() {
51
+ return fs.existsSync(PLAN_FILE);
52
+ }
53
+
54
+ // 解析 TASK_PLAN.md 中的任务
55
+ function parseTasksFromPlan() {
56
+ if (!fs.existsSync(PLAN_FILE)) {
57
+ return { research: [], develop: [], test: [] };
58
+ }
59
+
60
+ const content = fs.readFileSync(PLAN_FILE, 'utf-8');
61
+ const tasks = { research: [], develop: [], test: [] };
62
+
63
+ // 匹配任务项 - 支持 - [ ] 和 - [x] 格式
64
+ const taskRegex = /-\s*\[[ x]?\]\s*(?:\[([P0-3])\])?\s*(.+)/g;
65
+ let match;
66
+
67
+ // 当前任务类型上下文
68
+ let currentType = 'develop'; // 默认为开发任务
69
+
70
+ // 检测章节标题来确定任务类型
71
+ const lines = content.split('\n');
72
+ lines.forEach(line => {
73
+ const trimmed = line.trim();
74
+
75
+ // 检测章节标题
76
+ if (trimmed.includes('研究') || trimmed.includes('Research') || trimmed.includes('调研')) {
77
+ currentType = 'research';
78
+ } else if (trimmed.includes('开发') || trimmed.includes('Develop') || trimmed.includes('实现')) {
79
+ currentType = 'develop';
80
+ } else if (trimmed.includes('测试') || trimmed.includes('Test') || trimmed.includes('验证')) {
81
+ currentType = 'test';
82
+ }
83
+
84
+ // 匹配任务项
85
+ const taskMatch = trimmed.match(/^-\s*\[([ x])\]\s*(?:\[([P0-3])\])?\s*(.+)/);
86
+ if (taskMatch) {
87
+ const [, checked, priority, title] = taskMatch;
88
+ tasks[currentType].push({
89
+ title: title.trim(),
90
+ priority: priority || 'P2',
91
+ checked: checked === 'x'
92
+ });
93
+ }
94
+ });
95
+
96
+ return tasks;
97
+ }
98
+
99
+ // 创建任务文件
100
+ function createTaskFile(task, type, index) {
101
+ const typeConfig = TASK_TYPES[type];
102
+ const slug = titleToSlug(task.title);
103
+ const fileName = `${index}-${slug}.md`;
104
+ const filePath = path.join(BACKLOG_DIR, typeConfig.dir, fileName);
105
+ const now = new Date().toISOString().split('T')[0];
106
+
107
+ // 读取对应模板
108
+ const templatePath = path.join(__dirname, '../../../development/todos/_templates', `${typeConfig.dir}.md`);
109
+ let template = '';
110
+
111
+ if (fs.existsSync(templatePath)) {
112
+ template = fs.readFileSync(templatePath, 'utf-8');
113
+ } else {
114
+ // 简化模板
115
+ template = `# ${task.title}
116
+
117
+ > **类型**: ${typeConfig.icon} ${typeConfig.name} | ${getTypeDescription(type)}
118
+ > **状态**: 待规划
119
+ > **优先级**: ${task.priority}
120
+ > **创建时间**: ${now}
121
+ > **来源**: PROJECT_KICKOFF
122
+
123
+ ---
124
+
125
+ ## 📋 任务描述
126
+
127
+ ${task.title}
128
+
129
+ ---
130
+
131
+ ## 🎯 验收标准
132
+
133
+ - [ ] 验收标准 1
134
+ - [ ] 验收标准 2
135
+
136
+ ---
137
+
138
+ ## 📝 备注
139
+
140
+ 来自项目启动规划。
141
+ `;
142
+ }
143
+
144
+ // 创建目录
145
+ fs.mkdirSync(path.dirname(filePath), { recursive: true });
146
+
147
+ // 写入任务文件
148
+ fs.writeFileSync(filePath, template);
149
+
150
+ return fileName;
151
+ }
152
+
153
+ // 将标题转换为文件名友好的 slug
154
+ function titleToSlug(title) {
155
+ return title
156
+ .toLowerCase()
157
+ .replace(/[^\u4e00-\u9fa5a-z0-9]+/g, '-')
158
+ .replace(/^-+|-+$/g, '')
159
+ .substring(0, 50);
160
+ }
161
+
162
+ // 获取任务类型描述
163
+ function getTypeDescription(type) {
164
+ const descriptions = {
165
+ research: '调研/设计/探索',
166
+ develop: '实现/编码/重构',
167
+ test: '测试/验证/QA'
168
+ };
169
+ return descriptions[type] || '';
170
+ }
171
+
172
+ // 从规划创建任务到 backlog
173
+ function createTasksFromPlan() {
174
+ const tasks = parseTasksFromPlan();
175
+ let createdCount = 0;
176
+
177
+ // 确保目录存在
178
+ Object.values(TASK_TYPES).forEach(type => {
179
+ fs.mkdirSync(path.join(BACKLOG_DIR, type.dir), { recursive: true });
180
+ });
181
+
182
+ // 创建各类任务
183
+ Object.entries(tasks).forEach(([type, taskList]) => {
184
+ taskList.forEach((task, index) => {
185
+ if (!task.checked) { // 跳过已完成任务
186
+ const fileName = createTaskFile(task, type, index + 1);
187
+ createdCount++;
188
+ console.log(` ✅ 创建 ${type} 任务: ${fileName}`);
189
+ }
190
+ });
191
+ });
192
+
193
+ return createdCount;
194
+ }
195
+
196
+ // 刷新任务索引
197
+ function refreshTaskIndex() {
198
+ const todoManagerPath = path.join(PROJECT_DIR, '.claude/hooks/todo-manager.cjs');
199
+ if (fs.existsSync(todoManagerPath)) {
200
+ try {
201
+ execSync(`node "${todoManagerPath}" --force`, {
202
+ cwd: PROJECT_DIR,
203
+ stdio: 'pipe'
204
+ });
205
+ console.log(' ✅ 任务索引已更新');
206
+ } catch (e) {
207
+ // 忽略错误
208
+ }
209
+ }
210
+ }
211
+
38
212
  // 生成启动提示
39
213
  function generateKickoffHint() {
40
214
  const now = new Date().toISOString().split('T')[0];
@@ -83,7 +257,8 @@ function generateKickoffHint() {
83
257
  ✅ 生成 PROJECT_KICKOFF.md
84
258
  ✅ 生成 TASK_PLAN.md
85
259
  ✅ 生成 PROJECT_PROPOSAL.md
86
- 等待你的确认后开始执行
260
+ 🆕 自动创建任务到 development/todos/backlog/
261
+ ✅ 🆕 刷新任务索引
87
262
 
88
263
  ═══════════════════════════════════════════════════════════════════════
89
264
 
@@ -100,6 +275,20 @@ function main() {
100
275
  process.exit(0);
101
276
  }
102
277
 
278
+ // 如果已经有任务规划,自动创建任务
279
+ if (hasTaskPlan()) {
280
+ console.log('📋 检测到 TASK_PLAN.md,正在创建任务...');
281
+
282
+ const createdCount = createTasksFromPlan();
283
+
284
+ if (createdCount > 0) {
285
+ console.log(`✅ 已创建 ${createdCount} 个任务到 backlog/`);
286
+ refreshTaskIndex();
287
+ } else {
288
+ console.log('ℹ️ 所有任务已完成或为空');
289
+ }
290
+ }
291
+
103
292
  // 生成提示文件
104
293
  const hint = generateKickoffHint();
105
294
  fs.mkdirSync(path.dirname(HINT_FILE), { recursive: true });
@@ -0,0 +1,102 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Session Restore Hook - Restore conversation context from file
4
+ *
5
+ * Provides context continuity across sessions
6
+ */
7
+
8
+ const fs = require('fs');
9
+ const path = require('path');
10
+
11
+ const PROJECT_DIR = process.env.CLAUDE_PROJECT_DIR || process.cwd();
12
+ const SESSIONS_DIR = path.join(PROJECT_DIR, '.claude', 'sessions');
13
+ const MEMORY_FILE = path.join(PROJECT_DIR, '.claude', 'MEMORY.md');
14
+
15
+ /**
16
+ * Get latest session
17
+ */
18
+ function getLatestSession() {
19
+ if (!fs.existsSync(SESSIONS_DIR)) {
20
+ return null;
21
+ }
22
+
23
+ const files = fs.readdirSync(SESSIONS_DIR)
24
+ .filter(f => f.startsWith('session_') && f.endsWith('.md'))
25
+ .sort()
26
+ .reverse();
27
+
28
+ if (files.length === 0) {
29
+ return null;
30
+ }
31
+
32
+ const latestFile = files[0];
33
+ const filepath = path.join(SESSIONS_DIR, latestFile);
34
+ const content = fs.readFileSync(filepath, 'utf-8');
35
+
36
+ return {
37
+ file: latestFile,
38
+ path: filepath,
39
+ content: content
40
+ };
41
+ }
42
+
43
+ /**
44
+ * Get recent memory entries
45
+ */
46
+ function getRecentMemory(days = 7) {
47
+ if (!fs.existsSync(MEMORY_FILE)) {
48
+ return '';
49
+ }
50
+
51
+ const content = fs.readFileSync(MEMORY_FILE, 'utf-8');
52
+ const entries = content.split('## ').slice(1, days + 1);
53
+
54
+ return entries.join('## ');
55
+ }
56
+
57
+ /**
58
+ * Format context summary for display
59
+ */
60
+ function formatContextSummary(latestSession, recentMemory) {
61
+ let summary = '';
62
+
63
+ if (latestSession) {
64
+ summary += `\n📁 Last Session: ${latestSession.file}\n`;
65
+ summary += ` Date: ${fs.statSync(latestSession.path).mtime.toLocaleString()}\n`;
66
+ }
67
+
68
+ const memoryEntries = recentMemory.split('\n').filter(l => l.trim()).length;
69
+ if (memoryEntries > 0) {
70
+ summary += `\n💾 Memory Entries: ${memoryEntries}\n`;
71
+ }
72
+
73
+ return summary;
74
+ }
75
+
76
+ // Main execution
77
+ if (require.main === module) {
78
+ const args = process.argv.slice(2);
79
+
80
+ if (args[0] === '--latest') {
81
+ const session = getLatestSession();
82
+ if (session) {
83
+ console.log(session.content);
84
+ } else {
85
+ console.log('No sessions found.');
86
+ }
87
+ } else if (args[0] === '--memory') {
88
+ const memory = getRecentMemory(parseInt(args[1]) || 7);
89
+ console.log('# Recent Memory\n');
90
+ console.log(memory);
91
+ } else if (args[0] === '--summary') {
92
+ const session = getLatestSession();
93
+ const memory = getRecentMemory();
94
+ console.log(formatContextSummary(session, memory));
95
+ } else {
96
+ console.log('Usage: node session-restore.cjs [--latest|--memory|--summary]');
97
+ }
98
+ }
99
+
100
+ exports.getLatestSession = getLatestSession;
101
+ exports.getRecentMemory = getRecentMemory;
102
+ exports.formatContextSummary = formatContextSummary;