cc4pm 1.8.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.
Files changed (108) hide show
  1. package/.claude-plugin/README.md +17 -0
  2. package/.claude-plugin/plugin.json +25 -0
  3. package/LICENSE +21 -0
  4. package/README.md +157 -0
  5. package/README.zh-CN.md +134 -0
  6. package/contexts/dev.md +20 -0
  7. package/contexts/research.md +26 -0
  8. package/contexts/review.md +22 -0
  9. package/examples/CLAUDE.md +100 -0
  10. package/examples/statusline.json +19 -0
  11. package/examples/user-CLAUDE.md +109 -0
  12. package/install.sh +17 -0
  13. package/manifests/install-components.json +173 -0
  14. package/manifests/install-modules.json +335 -0
  15. package/manifests/install-profiles.json +75 -0
  16. package/package.json +117 -0
  17. package/schemas/ecc-install-config.schema.json +58 -0
  18. package/schemas/hooks.schema.json +197 -0
  19. package/schemas/install-components.schema.json +56 -0
  20. package/schemas/install-modules.schema.json +105 -0
  21. package/schemas/install-profiles.schema.json +45 -0
  22. package/schemas/install-state.schema.json +210 -0
  23. package/schemas/package-manager.schema.json +23 -0
  24. package/schemas/plugin.schema.json +58 -0
  25. package/scripts/ci/catalog.js +83 -0
  26. package/scripts/ci/validate-agents.js +81 -0
  27. package/scripts/ci/validate-commands.js +135 -0
  28. package/scripts/ci/validate-hooks.js +239 -0
  29. package/scripts/ci/validate-install-manifests.js +211 -0
  30. package/scripts/ci/validate-no-personal-paths.js +63 -0
  31. package/scripts/ci/validate-rules.js +81 -0
  32. package/scripts/ci/validate-skills.js +54 -0
  33. package/scripts/claw.js +468 -0
  34. package/scripts/doctor.js +110 -0
  35. package/scripts/ecc.js +194 -0
  36. package/scripts/hooks/auto-tmux-dev.js +88 -0
  37. package/scripts/hooks/check-console-log.js +71 -0
  38. package/scripts/hooks/check-hook-enabled.js +12 -0
  39. package/scripts/hooks/cost-tracker.js +78 -0
  40. package/scripts/hooks/doc-file-warning.js +63 -0
  41. package/scripts/hooks/evaluate-session.js +100 -0
  42. package/scripts/hooks/insaits-security-monitor.py +269 -0
  43. package/scripts/hooks/insaits-security-wrapper.js +88 -0
  44. package/scripts/hooks/post-bash-build-complete.js +27 -0
  45. package/scripts/hooks/post-bash-pr-created.js +36 -0
  46. package/scripts/hooks/post-edit-console-warn.js +54 -0
  47. package/scripts/hooks/post-edit-format.js +109 -0
  48. package/scripts/hooks/post-edit-typecheck.js +96 -0
  49. package/scripts/hooks/pre-bash-dev-server-block.js +187 -0
  50. package/scripts/hooks/pre-bash-git-push-reminder.js +28 -0
  51. package/scripts/hooks/pre-bash-tmux-reminder.js +33 -0
  52. package/scripts/hooks/pre-compact.js +48 -0
  53. package/scripts/hooks/pre-write-doc-warn.js +9 -0
  54. package/scripts/hooks/quality-gate.js +168 -0
  55. package/scripts/hooks/run-with-flags-shell.sh +32 -0
  56. package/scripts/hooks/run-with-flags.js +120 -0
  57. package/scripts/hooks/session-end-marker.js +15 -0
  58. package/scripts/hooks/session-end.js +299 -0
  59. package/scripts/hooks/session-start.js +97 -0
  60. package/scripts/hooks/suggest-compact.js +80 -0
  61. package/scripts/install-apply.js +137 -0
  62. package/scripts/install-plan.js +254 -0
  63. package/scripts/lib/hook-flags.js +74 -0
  64. package/scripts/lib/install/apply.js +23 -0
  65. package/scripts/lib/install/config.js +82 -0
  66. package/scripts/lib/install/request.js +113 -0
  67. package/scripts/lib/install/runtime.js +42 -0
  68. package/scripts/lib/install-executor.js +605 -0
  69. package/scripts/lib/install-lifecycle.js +763 -0
  70. package/scripts/lib/install-manifests.js +305 -0
  71. package/scripts/lib/install-state.js +120 -0
  72. package/scripts/lib/install-targets/antigravity-project.js +9 -0
  73. package/scripts/lib/install-targets/claude-home.js +10 -0
  74. package/scripts/lib/install-targets/codex-home.js +10 -0
  75. package/scripts/lib/install-targets/cursor-project.js +10 -0
  76. package/scripts/lib/install-targets/helpers.js +89 -0
  77. package/scripts/lib/install-targets/opencode-home.js +10 -0
  78. package/scripts/lib/install-targets/registry.js +64 -0
  79. package/scripts/lib/orchestration-session.js +299 -0
  80. package/scripts/lib/package-manager.d.ts +119 -0
  81. package/scripts/lib/package-manager.js +431 -0
  82. package/scripts/lib/project-detect.js +428 -0
  83. package/scripts/lib/resolve-formatter.js +185 -0
  84. package/scripts/lib/session-adapters/canonical-session.js +138 -0
  85. package/scripts/lib/session-adapters/claude-history.js +149 -0
  86. package/scripts/lib/session-adapters/dmux-tmux.js +80 -0
  87. package/scripts/lib/session-adapters/registry.js +111 -0
  88. package/scripts/lib/session-aliases.d.ts +136 -0
  89. package/scripts/lib/session-aliases.js +481 -0
  90. package/scripts/lib/session-manager.d.ts +131 -0
  91. package/scripts/lib/session-manager.js +464 -0
  92. package/scripts/lib/shell-split.js +86 -0
  93. package/scripts/lib/skill-improvement/amendify.js +89 -0
  94. package/scripts/lib/skill-improvement/evaluate.js +59 -0
  95. package/scripts/lib/skill-improvement/health.js +118 -0
  96. package/scripts/lib/skill-improvement/observations.js +108 -0
  97. package/scripts/lib/tmux-worktree-orchestrator.js +491 -0
  98. package/scripts/lib/utils.d.ts +183 -0
  99. package/scripts/lib/utils.js +543 -0
  100. package/scripts/list-installed.js +90 -0
  101. package/scripts/orchestrate-codex-worker.sh +92 -0
  102. package/scripts/orchestrate-worktrees.js +108 -0
  103. package/scripts/orchestration-status.js +62 -0
  104. package/scripts/repair.js +97 -0
  105. package/scripts/session-inspect.js +150 -0
  106. package/scripts/setup-package-manager.js +204 -0
  107. package/scripts/skill-create-output.js +244 -0
  108. package/scripts/uninstall.js +96 -0
@@ -0,0 +1,481 @@
1
+ /**
2
+ * Session Aliases Library for Claude Code
3
+ * Manages session aliases stored in ~/.claude/session-aliases.json
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+
9
+ const {
10
+ getClaudeDir,
11
+ ensureDir,
12
+ readFile,
13
+ log
14
+ } = require('./utils');
15
+
16
+ // Aliases file path
17
+ function getAliasesPath() {
18
+ return path.join(getClaudeDir(), 'session-aliases.json');
19
+ }
20
+
21
+ // Current alias storage format version
22
+ const ALIAS_VERSION = '1.0';
23
+
24
+ /**
25
+ * Default aliases file structure
26
+ */
27
+ function getDefaultAliases() {
28
+ return {
29
+ version: ALIAS_VERSION,
30
+ aliases: {},
31
+ metadata: {
32
+ totalCount: 0,
33
+ lastUpdated: new Date().toISOString()
34
+ }
35
+ };
36
+ }
37
+
38
+ /**
39
+ * Load aliases from file
40
+ * @returns {object} Aliases object
41
+ */
42
+ function loadAliases() {
43
+ const aliasesPath = getAliasesPath();
44
+
45
+ if (!fs.existsSync(aliasesPath)) {
46
+ return getDefaultAliases();
47
+ }
48
+
49
+ const content = readFile(aliasesPath);
50
+ if (!content) {
51
+ return getDefaultAliases();
52
+ }
53
+
54
+ try {
55
+ const data = JSON.parse(content);
56
+
57
+ // Validate structure
58
+ if (!data.aliases || typeof data.aliases !== 'object') {
59
+ log('[Aliases] Invalid aliases file structure, resetting');
60
+ return getDefaultAliases();
61
+ }
62
+
63
+ // Ensure version field
64
+ if (!data.version) {
65
+ data.version = ALIAS_VERSION;
66
+ }
67
+
68
+ // Ensure metadata
69
+ if (!data.metadata) {
70
+ data.metadata = {
71
+ totalCount: Object.keys(data.aliases).length,
72
+ lastUpdated: new Date().toISOString()
73
+ };
74
+ }
75
+
76
+ return data;
77
+ } catch (err) {
78
+ log(`[Aliases] Error parsing aliases file: ${err.message}`);
79
+ return getDefaultAliases();
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Save aliases to file with atomic write
85
+ * @param {object} aliases - Aliases object to save
86
+ * @returns {boolean} Success status
87
+ */
88
+ function saveAliases(aliases) {
89
+ const aliasesPath = getAliasesPath();
90
+ const tempPath = aliasesPath + '.tmp';
91
+ const backupPath = aliasesPath + '.bak';
92
+
93
+ try {
94
+ // Update metadata
95
+ aliases.metadata = {
96
+ totalCount: Object.keys(aliases.aliases).length,
97
+ lastUpdated: new Date().toISOString()
98
+ };
99
+
100
+ const content = JSON.stringify(aliases, null, 2);
101
+
102
+ // Ensure directory exists
103
+ ensureDir(path.dirname(aliasesPath));
104
+
105
+ // Create backup if file exists
106
+ if (fs.existsSync(aliasesPath)) {
107
+ fs.copyFileSync(aliasesPath, backupPath);
108
+ }
109
+
110
+ // Atomic write: write to temp file, then rename
111
+ fs.writeFileSync(tempPath, content, 'utf8');
112
+
113
+ // On Windows, rename fails with EEXIST if destination exists, so delete first.
114
+ // On Unix/macOS, rename(2) atomically replaces the destination — skip the
115
+ // delete to avoid an unnecessary non-atomic window between unlink and rename.
116
+ if (process.platform === 'win32' && fs.existsSync(aliasesPath)) {
117
+ fs.unlinkSync(aliasesPath);
118
+ }
119
+ fs.renameSync(tempPath, aliasesPath);
120
+
121
+ // Remove backup on success
122
+ if (fs.existsSync(backupPath)) {
123
+ fs.unlinkSync(backupPath);
124
+ }
125
+
126
+ return true;
127
+ } catch (err) {
128
+ log(`[Aliases] Error saving aliases: ${err.message}`);
129
+
130
+ // Restore from backup if exists
131
+ if (fs.existsSync(backupPath)) {
132
+ try {
133
+ fs.copyFileSync(backupPath, aliasesPath);
134
+ log('[Aliases] Restored from backup');
135
+ } catch (restoreErr) {
136
+ log(`[Aliases] Failed to restore backup: ${restoreErr.message}`);
137
+ }
138
+ }
139
+
140
+ // Clean up temp file (best-effort)
141
+ try {
142
+ if (fs.existsSync(tempPath)) {
143
+ fs.unlinkSync(tempPath);
144
+ }
145
+ } catch {
146
+ // Non-critical: temp file will be overwritten on next save
147
+ }
148
+
149
+ return false;
150
+ }
151
+ }
152
+
153
+ /**
154
+ * Resolve an alias to get session path
155
+ * @param {string} alias - Alias name to resolve
156
+ * @returns {object|null} Alias data or null if not found
157
+ */
158
+ function resolveAlias(alias) {
159
+ if (!alias) return null;
160
+
161
+ // Validate alias name (alphanumeric, dash, underscore)
162
+ if (!/^[a-zA-Z0-9_-]+$/.test(alias)) {
163
+ return null;
164
+ }
165
+
166
+ const data = loadAliases();
167
+ const aliasData = data.aliases[alias];
168
+
169
+ if (!aliasData) {
170
+ return null;
171
+ }
172
+
173
+ return {
174
+ alias,
175
+ sessionPath: aliasData.sessionPath,
176
+ createdAt: aliasData.createdAt,
177
+ title: aliasData.title || null
178
+ };
179
+ }
180
+
181
+ /**
182
+ * Set or update an alias for a session
183
+ * @param {string} alias - Alias name (alphanumeric, dash, underscore)
184
+ * @param {string} sessionPath - Session directory path
185
+ * @param {string} title - Optional title for the alias
186
+ * @returns {object} Result with success status and message
187
+ */
188
+ function setAlias(alias, sessionPath, title = null) {
189
+ // Validate alias name
190
+ if (!alias || alias.length === 0) {
191
+ return { success: false, error: 'Alias name cannot be empty' };
192
+ }
193
+
194
+ // Validate session path
195
+ if (!sessionPath || typeof sessionPath !== 'string' || sessionPath.trim().length === 0) {
196
+ return { success: false, error: 'Session path cannot be empty' };
197
+ }
198
+
199
+ if (alias.length > 128) {
200
+ return { success: false, error: 'Alias name cannot exceed 128 characters' };
201
+ }
202
+
203
+ if (!/^[a-zA-Z0-9_-]+$/.test(alias)) {
204
+ return { success: false, error: 'Alias name must contain only letters, numbers, dashes, and underscores' };
205
+ }
206
+
207
+ // Reserved alias names
208
+ const reserved = ['list', 'help', 'remove', 'delete', 'create', 'set'];
209
+ if (reserved.includes(alias.toLowerCase())) {
210
+ return { success: false, error: `'${alias}' is a reserved alias name` };
211
+ }
212
+
213
+ const data = loadAliases();
214
+ const existing = data.aliases[alias];
215
+ const isNew = !existing;
216
+
217
+ data.aliases[alias] = {
218
+ sessionPath,
219
+ createdAt: existing ? existing.createdAt : new Date().toISOString(),
220
+ updatedAt: new Date().toISOString(),
221
+ title: title || null
222
+ };
223
+
224
+ if (saveAliases(data)) {
225
+ return {
226
+ success: true,
227
+ isNew,
228
+ alias,
229
+ sessionPath,
230
+ title: data.aliases[alias].title
231
+ };
232
+ }
233
+
234
+ return { success: false, error: 'Failed to save alias' };
235
+ }
236
+
237
+ /**
238
+ * List all aliases
239
+ * @param {object} options - Options object
240
+ * @param {string} options.search - Filter aliases by name (partial match)
241
+ * @param {number} options.limit - Maximum number of aliases to return
242
+ * @returns {Array} Array of alias objects
243
+ */
244
+ function listAliases(options = {}) {
245
+ const { search = null, limit = null } = options;
246
+ const data = loadAliases();
247
+
248
+ let aliases = Object.entries(data.aliases).map(([name, info]) => ({
249
+ name,
250
+ sessionPath: info.sessionPath,
251
+ createdAt: info.createdAt,
252
+ updatedAt: info.updatedAt,
253
+ title: info.title
254
+ }));
255
+
256
+ // Sort by updated time (newest first)
257
+ aliases.sort((a, b) => (new Date(b.updatedAt || b.createdAt || 0).getTime() || 0) - (new Date(a.updatedAt || a.createdAt || 0).getTime() || 0));
258
+
259
+ // Apply search filter
260
+ if (search) {
261
+ const searchLower = search.toLowerCase();
262
+ aliases = aliases.filter(a =>
263
+ a.name.toLowerCase().includes(searchLower) ||
264
+ (a.title && a.title.toLowerCase().includes(searchLower))
265
+ );
266
+ }
267
+
268
+ // Apply limit
269
+ if (limit && limit > 0) {
270
+ aliases = aliases.slice(0, limit);
271
+ }
272
+
273
+ return aliases;
274
+ }
275
+
276
+ /**
277
+ * Delete an alias
278
+ * @param {string} alias - Alias name to delete
279
+ * @returns {object} Result with success status
280
+ */
281
+ function deleteAlias(alias) {
282
+ const data = loadAliases();
283
+
284
+ if (!data.aliases[alias]) {
285
+ return { success: false, error: `Alias '${alias}' not found` };
286
+ }
287
+
288
+ const deleted = data.aliases[alias];
289
+ delete data.aliases[alias];
290
+
291
+ if (saveAliases(data)) {
292
+ return {
293
+ success: true,
294
+ alias,
295
+ deletedSessionPath: deleted.sessionPath
296
+ };
297
+ }
298
+
299
+ return { success: false, error: 'Failed to delete alias' };
300
+ }
301
+
302
+ /**
303
+ * Rename an alias
304
+ * @param {string} oldAlias - Current alias name
305
+ * @param {string} newAlias - New alias name
306
+ * @returns {object} Result with success status
307
+ */
308
+ function renameAlias(oldAlias, newAlias) {
309
+ const data = loadAliases();
310
+
311
+ if (!data.aliases[oldAlias]) {
312
+ return { success: false, error: `Alias '${oldAlias}' not found` };
313
+ }
314
+
315
+ // Validate new alias name (same rules as setAlias)
316
+ if (!newAlias || newAlias.length === 0) {
317
+ return { success: false, error: 'New alias name cannot be empty' };
318
+ }
319
+
320
+ if (newAlias.length > 128) {
321
+ return { success: false, error: 'New alias name cannot exceed 128 characters' };
322
+ }
323
+
324
+ if (!/^[a-zA-Z0-9_-]+$/.test(newAlias)) {
325
+ return { success: false, error: 'New alias name must contain only letters, numbers, dashes, and underscores' };
326
+ }
327
+
328
+ const reserved = ['list', 'help', 'remove', 'delete', 'create', 'set'];
329
+ if (reserved.includes(newAlias.toLowerCase())) {
330
+ return { success: false, error: `'${newAlias}' is a reserved alias name` };
331
+ }
332
+
333
+ if (data.aliases[newAlias]) {
334
+ return { success: false, error: `Alias '${newAlias}' already exists` };
335
+ }
336
+
337
+ const aliasData = data.aliases[oldAlias];
338
+ delete data.aliases[oldAlias];
339
+
340
+ aliasData.updatedAt = new Date().toISOString();
341
+ data.aliases[newAlias] = aliasData;
342
+
343
+ if (saveAliases(data)) {
344
+ return {
345
+ success: true,
346
+ oldAlias,
347
+ newAlias,
348
+ sessionPath: aliasData.sessionPath
349
+ };
350
+ }
351
+
352
+ // Restore old alias and remove new alias on failure
353
+ data.aliases[oldAlias] = aliasData;
354
+ delete data.aliases[newAlias];
355
+ // Attempt to persist the rollback
356
+ saveAliases(data);
357
+ return { success: false, error: 'Failed to save renamed alias — rolled back to original' };
358
+ }
359
+
360
+ /**
361
+ * Get session path by alias (convenience function)
362
+ * @param {string} aliasOrId - Alias name or session ID
363
+ * @returns {string|null} Session path or null if not found
364
+ */
365
+ function resolveSessionAlias(aliasOrId) {
366
+ // First try to resolve as alias
367
+ const resolved = resolveAlias(aliasOrId);
368
+ if (resolved) {
369
+ return resolved.sessionPath;
370
+ }
371
+
372
+ // If not an alias, return as-is (might be a session path)
373
+ return aliasOrId;
374
+ }
375
+
376
+ /**
377
+ * Update alias title
378
+ * @param {string} alias - Alias name
379
+ * @param {string|null} title - New title (string or null to clear)
380
+ * @returns {object} Result with success status
381
+ */
382
+ function updateAliasTitle(alias, title) {
383
+ if (title !== null && typeof title !== 'string') {
384
+ return { success: false, error: 'Title must be a string or null' };
385
+ }
386
+
387
+ const data = loadAliases();
388
+
389
+ if (!data.aliases[alias]) {
390
+ return { success: false, error: `Alias '${alias}' not found` };
391
+ }
392
+
393
+ data.aliases[alias].title = title || null;
394
+ data.aliases[alias].updatedAt = new Date().toISOString();
395
+
396
+ if (saveAliases(data)) {
397
+ return {
398
+ success: true,
399
+ alias,
400
+ title
401
+ };
402
+ }
403
+
404
+ return { success: false, error: 'Failed to update alias title' };
405
+ }
406
+
407
+ /**
408
+ * Get all aliases for a specific session
409
+ * @param {string} sessionPath - Session path to find aliases for
410
+ * @returns {Array} Array of alias names
411
+ */
412
+ function getAliasesForSession(sessionPath) {
413
+ const data = loadAliases();
414
+ const aliases = [];
415
+
416
+ for (const [name, info] of Object.entries(data.aliases)) {
417
+ if (info.sessionPath === sessionPath) {
418
+ aliases.push({
419
+ name,
420
+ createdAt: info.createdAt,
421
+ title: info.title
422
+ });
423
+ }
424
+ }
425
+
426
+ return aliases;
427
+ }
428
+
429
+ /**
430
+ * Clean up aliases for non-existent sessions
431
+ * @param {Function} sessionExists - Function to check if session exists
432
+ * @returns {object} Cleanup result
433
+ */
434
+ function cleanupAliases(sessionExists) {
435
+ if (typeof sessionExists !== 'function') {
436
+ return { totalChecked: 0, removed: 0, removedAliases: [], error: 'sessionExists must be a function' };
437
+ }
438
+
439
+ const data = loadAliases();
440
+ const removed = [];
441
+
442
+ for (const [name, info] of Object.entries(data.aliases)) {
443
+ if (!sessionExists(info.sessionPath)) {
444
+ removed.push({ name, sessionPath: info.sessionPath });
445
+ delete data.aliases[name];
446
+ }
447
+ }
448
+
449
+ if (removed.length > 0 && !saveAliases(data)) {
450
+ log('[Aliases] Failed to save after cleanup');
451
+ return {
452
+ success: false,
453
+ totalChecked: Object.keys(data.aliases).length + removed.length,
454
+ removed: removed.length,
455
+ removedAliases: removed,
456
+ error: 'Failed to save after cleanup'
457
+ };
458
+ }
459
+
460
+ return {
461
+ success: true,
462
+ totalChecked: Object.keys(data.aliases).length + removed.length,
463
+ removed: removed.length,
464
+ removedAliases: removed
465
+ };
466
+ }
467
+
468
+ module.exports = {
469
+ getAliasesPath,
470
+ loadAliases,
471
+ saveAliases,
472
+ resolveAlias,
473
+ setAlias,
474
+ listAliases,
475
+ deleteAlias,
476
+ renameAlias,
477
+ resolveSessionAlias,
478
+ updateAliasTitle,
479
+ getAliasesForSession,
480
+ cleanupAliases
481
+ };
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Session Manager Library for Claude Code.
3
+ * Provides CRUD operations for session files stored as markdown in ~/.claude/sessions/.
4
+ */
5
+
6
+ /** Parsed metadata from a session filename */
7
+ export interface SessionFilenameMeta {
8
+ /** Original filename */
9
+ filename: string;
10
+ /** Short ID extracted from filename, or "no-id" for old format */
11
+ shortId: string;
12
+ /** Date string in YYYY-MM-DD format */
13
+ date: string;
14
+ /** Parsed Date object from the date string */
15
+ datetime: Date;
16
+ }
17
+
18
+ /** Metadata parsed from session markdown content */
19
+ export interface SessionMetadata {
20
+ title: string | null;
21
+ date: string | null;
22
+ started: string | null;
23
+ lastUpdated: string | null;
24
+ completed: string[];
25
+ inProgress: string[];
26
+ notes: string;
27
+ context: string;
28
+ }
29
+
30
+ /** Statistics computed from session content */
31
+ export interface SessionStats {
32
+ totalItems: number;
33
+ completedItems: number;
34
+ inProgressItems: number;
35
+ lineCount: number;
36
+ hasNotes: boolean;
37
+ hasContext: boolean;
38
+ }
39
+
40
+ /** A session object returned by getAllSessions and getSessionById */
41
+ export interface Session extends SessionFilenameMeta {
42
+ /** Full filesystem path to the session file */
43
+ sessionPath: string;
44
+ /** Whether the file has any content */
45
+ hasContent?: boolean;
46
+ /** File size in bytes */
47
+ size: number;
48
+ /** Last modification time */
49
+ modifiedTime: Date;
50
+ /** File creation time (falls back to ctime on Linux) */
51
+ createdTime: Date;
52
+ /** Session markdown content (only when includeContent=true) */
53
+ content?: string | null;
54
+ /** Parsed metadata (only when includeContent=true) */
55
+ metadata?: SessionMetadata;
56
+ /** Session statistics (only when includeContent=true) */
57
+ stats?: SessionStats;
58
+ }
59
+
60
+ /** Pagination result from getAllSessions */
61
+ export interface SessionListResult {
62
+ sessions: Session[];
63
+ total: number;
64
+ offset: number;
65
+ limit: number;
66
+ hasMore: boolean;
67
+ }
68
+
69
+ export interface GetAllSessionsOptions {
70
+ /** Maximum number of sessions to return (default: 50) */
71
+ limit?: number;
72
+ /** Number of sessions to skip (default: 0) */
73
+ offset?: number;
74
+ /** Filter by date in YYYY-MM-DD format */
75
+ date?: string | null;
76
+ /** Search in short ID */
77
+ search?: string | null;
78
+ }
79
+
80
+ /**
81
+ * Parse a session filename to extract date and short ID.
82
+ * @returns Parsed metadata, or null if the filename doesn't match the expected pattern
83
+ */
84
+ export function parseSessionFilename(filename: string): SessionFilenameMeta | null;
85
+
86
+ /** Get the full filesystem path for a session filename */
87
+ export function getSessionPath(filename: string): string;
88
+
89
+ /**
90
+ * Read session markdown content from disk.
91
+ * @returns Content string, or null if the file doesn't exist
92
+ */
93
+ export function getSessionContent(sessionPath: string): string | null;
94
+
95
+ /** Parse session metadata from markdown content */
96
+ export function parseSessionMetadata(content: string | null): SessionMetadata;
97
+
98
+ /**
99
+ * Calculate statistics for a session.
100
+ * Accepts either a file path (absolute, ending in .tmp) or pre-read content string.
101
+ * Supports both Unix (/path/to/session.tmp) and Windows (C:\path\to\session.tmp) paths.
102
+ */
103
+ export function getSessionStats(sessionPathOrContent: string): SessionStats;
104
+
105
+ /** Get the title from a session file, or "Untitled Session" if none */
106
+ export function getSessionTitle(sessionPath: string): string;
107
+
108
+ /** Get human-readable file size (e.g., "1.2 KB") */
109
+ export function getSessionSize(sessionPath: string): string;
110
+
111
+ /** Get all sessions with optional filtering and pagination */
112
+ export function getAllSessions(options?: GetAllSessionsOptions): SessionListResult;
113
+
114
+ /**
115
+ * Find a session by short ID or filename.
116
+ * @param sessionId - Short ID prefix, full filename, or filename without .tmp
117
+ * @param includeContent - Whether to read and parse the session content
118
+ */
119
+ export function getSessionById(sessionId: string, includeContent?: boolean): Session | null;
120
+
121
+ /** Write markdown content to a session file */
122
+ export function writeSessionContent(sessionPath: string, content: string): boolean;
123
+
124
+ /** Append content to an existing session file */
125
+ export function appendSessionContent(sessionPath: string, content: string): boolean;
126
+
127
+ /** Delete a session file */
128
+ export function deleteSession(sessionPath: string): boolean;
129
+
130
+ /** Check if a session file exists and is a regular file */
131
+ export function sessionExists(sessionPath: string): boolean;