everything-claude-code 1.4.3

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 (57) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +739 -0
  3. package/README.zh-CN.md +523 -0
  4. package/crates/ecc-kernel/Cargo.lock +160 -0
  5. package/crates/ecc-kernel/Cargo.toml +15 -0
  6. package/crates/ecc-kernel/src/main.rs +710 -0
  7. package/docs/ecc.md +117 -0
  8. package/package.json +45 -0
  9. package/packs/blueprint.json +8 -0
  10. package/packs/forge.json +16 -0
  11. package/packs/instinct.json +16 -0
  12. package/packs/orchestra.json +15 -0
  13. package/packs/proof.json +8 -0
  14. package/packs/sentinel.json +8 -0
  15. package/prompts/ecc/patch.md +25 -0
  16. package/prompts/ecc/plan.md +28 -0
  17. package/schemas/ecc.apply.schema.json +35 -0
  18. package/schemas/ecc.config.schema.json +37 -0
  19. package/schemas/ecc.lock.schema.json +34 -0
  20. package/schemas/ecc.patch.schema.json +25 -0
  21. package/schemas/ecc.plan.schema.json +32 -0
  22. package/schemas/ecc.run.schema.json +67 -0
  23. package/schemas/ecc.verify.schema.json +27 -0
  24. package/schemas/hooks.schema.json +81 -0
  25. package/schemas/package-manager.schema.json +17 -0
  26. package/schemas/plugin.schema.json +13 -0
  27. package/scripts/ecc/catalog.js +82 -0
  28. package/scripts/ecc/config.js +43 -0
  29. package/scripts/ecc/diff.js +113 -0
  30. package/scripts/ecc/exec.js +121 -0
  31. package/scripts/ecc/fixtures/basic/patches/impl-core.diff +8 -0
  32. package/scripts/ecc/fixtures/basic/patches/tests.diff +8 -0
  33. package/scripts/ecc/fixtures/basic/plan.json +23 -0
  34. package/scripts/ecc/fixtures/unauthorized/patches/impl-core.diff +8 -0
  35. package/scripts/ecc/fixtures/unauthorized/plan.json +15 -0
  36. package/scripts/ecc/git.js +139 -0
  37. package/scripts/ecc/id.js +37 -0
  38. package/scripts/ecc/install-kernel.js +344 -0
  39. package/scripts/ecc/json-extract.js +301 -0
  40. package/scripts/ecc/json.js +26 -0
  41. package/scripts/ecc/kernel.js +144 -0
  42. package/scripts/ecc/lock.js +36 -0
  43. package/scripts/ecc/paths.js +28 -0
  44. package/scripts/ecc/plan.js +57 -0
  45. package/scripts/ecc/project.js +37 -0
  46. package/scripts/ecc/providers/codex.js +168 -0
  47. package/scripts/ecc/providers/index.js +23 -0
  48. package/scripts/ecc/providers/mock.js +49 -0
  49. package/scripts/ecc/report.js +127 -0
  50. package/scripts/ecc/run.js +105 -0
  51. package/scripts/ecc/validate.js +325 -0
  52. package/scripts/ecc/verify.js +125 -0
  53. package/scripts/ecc.js +532 -0
  54. package/scripts/lib/package-manager.js +390 -0
  55. package/scripts/lib/session-aliases.js +432 -0
  56. package/scripts/lib/session-manager.js +396 -0
  57. package/scripts/lib/utils.js +426 -0
@@ -0,0 +1,396 @@
1
+ /**
2
+ * Session Manager Library for Claude Code
3
+ * Provides core session CRUD operations for listing, loading, and managing sessions
4
+ *
5
+ * Sessions are stored as markdown files in ~/.claude/sessions/ with format:
6
+ * - YYYY-MM-DD-session.tmp (old format)
7
+ * - YYYY-MM-DD-<short-id>-session.tmp (new format)
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ const {
14
+ getSessionsDir,
15
+ readFile,
16
+ log
17
+ } = require('./utils');
18
+
19
+ // Session filename pattern: YYYY-MM-DD-[short-id]-session.tmp
20
+ // The short-id is optional (old format) and can be 8+ alphanumeric characters
21
+ // Matches: "2026-02-01-session.tmp" or "2026-02-01-a1b2c3d4-session.tmp"
22
+ const SESSION_FILENAME_REGEX = /^(\d{4}-\d{2}-\d{2})(?:-([a-z0-9]{8,}))?-session\.tmp$/;
23
+
24
+ /**
25
+ * Parse session filename to extract metadata
26
+ * @param {string} filename - Session filename (e.g., "2026-01-17-abc123-session.tmp" or "2026-01-17-session.tmp")
27
+ * @returns {object|null} Parsed metadata or null if invalid
28
+ */
29
+ function parseSessionFilename(filename) {
30
+ const match = filename.match(SESSION_FILENAME_REGEX);
31
+ if (!match) return null;
32
+
33
+ const dateStr = match[1];
34
+ // match[2] is undefined for old format (no ID)
35
+ const shortId = match[2] || 'no-id';
36
+
37
+ return {
38
+ filename,
39
+ shortId,
40
+ date: dateStr,
41
+ // Convert date string to Date object
42
+ datetime: new Date(dateStr)
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Get the full path to a session file
48
+ * @param {string} filename - Session filename
49
+ * @returns {string} Full path to session file
50
+ */
51
+ function getSessionPath(filename) {
52
+ return path.join(getSessionsDir(), filename);
53
+ }
54
+
55
+ /**
56
+ * Read and parse session markdown content
57
+ * @param {string} sessionPath - Full path to session file
58
+ * @returns {string|null} Session content or null if not found
59
+ */
60
+ function getSessionContent(sessionPath) {
61
+ if (!fs.existsSync(sessionPath)) {
62
+ return null;
63
+ }
64
+
65
+ return readFile(sessionPath);
66
+ }
67
+
68
+ /**
69
+ * Parse session metadata from markdown content
70
+ * @param {string} content - Session markdown content
71
+ * @returns {object} Parsed metadata
72
+ */
73
+ function parseSessionMetadata(content) {
74
+ const metadata = {
75
+ title: null,
76
+ date: null,
77
+ started: null,
78
+ lastUpdated: null,
79
+ completed: [],
80
+ inProgress: [],
81
+ notes: '',
82
+ context: ''
83
+ };
84
+
85
+ if (!content) return metadata;
86
+
87
+ // Extract title from first heading
88
+ const titleMatch = content.match(/^#\s+(.+)$/m);
89
+ if (titleMatch) {
90
+ metadata.title = titleMatch[1].trim();
91
+ }
92
+
93
+ // Extract date
94
+ const dateMatch = content.match(/\*\*Date:\*\*\s*(\d{4}-\d{2}-\d{2})/);
95
+ if (dateMatch) {
96
+ metadata.date = dateMatch[1];
97
+ }
98
+
99
+ // Extract started time
100
+ const startedMatch = content.match(/\*\*Started:\*\*\s*([\d:]+)/);
101
+ if (startedMatch) {
102
+ metadata.started = startedMatch[1];
103
+ }
104
+
105
+ // Extract last updated
106
+ const updatedMatch = content.match(/\*\*Last Updated:\*\*\s*([\d:]+)/);
107
+ if (updatedMatch) {
108
+ metadata.lastUpdated = updatedMatch[1];
109
+ }
110
+
111
+ // Extract completed items
112
+ const completedSection = content.match(/### Completed\s*\n([\s\S]*?)(?=###|\n\n|$)/);
113
+ if (completedSection) {
114
+ const items = completedSection[1].match(/- \[x\]\s*(.+)/g);
115
+ if (items) {
116
+ metadata.completed = items.map(item => item.replace(/- \[x\]\s*/, '').trim());
117
+ }
118
+ }
119
+
120
+ // Extract in-progress items
121
+ const progressSection = content.match(/### In Progress\s*\n([\s\S]*?)(?=###|\n\n|$)/);
122
+ if (progressSection) {
123
+ const items = progressSection[1].match(/- \[ \]\s*(.+)/g);
124
+ if (items) {
125
+ metadata.inProgress = items.map(item => item.replace(/- \[ \]\s*/, '').trim());
126
+ }
127
+ }
128
+
129
+ // Extract notes
130
+ const notesSection = content.match(/### Notes for Next Session\s*\n([\s\S]*?)(?=###|\n\n|$)/);
131
+ if (notesSection) {
132
+ metadata.notes = notesSection[1].trim();
133
+ }
134
+
135
+ // Extract context to load
136
+ const contextSection = content.match(/### Context to Load\s*\n```\n([\s\S]*?)```/);
137
+ if (contextSection) {
138
+ metadata.context = contextSection[1].trim();
139
+ }
140
+
141
+ return metadata;
142
+ }
143
+
144
+ /**
145
+ * Calculate statistics for a session
146
+ * @param {string} sessionPath - Full path to session file
147
+ * @returns {object} Statistics object
148
+ */
149
+ function getSessionStats(sessionPath) {
150
+ const content = getSessionContent(sessionPath);
151
+ const metadata = parseSessionMetadata(content);
152
+
153
+ return {
154
+ totalItems: metadata.completed.length + metadata.inProgress.length,
155
+ completedItems: metadata.completed.length,
156
+ inProgressItems: metadata.inProgress.length,
157
+ lineCount: content ? content.split('\n').length : 0,
158
+ hasNotes: !!metadata.notes,
159
+ hasContext: !!metadata.context
160
+ };
161
+ }
162
+
163
+ /**
164
+ * Get all sessions with optional filtering and pagination
165
+ * @param {object} options - Options object
166
+ * @param {number} options.limit - Maximum number of sessions to return
167
+ * @param {number} options.offset - Number of sessions to skip
168
+ * @param {string} options.date - Filter by date (YYYY-MM-DD format)
169
+ * @param {string} options.search - Search in short ID
170
+ * @returns {object} Object with sessions array and pagination info
171
+ */
172
+ function getAllSessions(options = {}) {
173
+ const {
174
+ limit = 50,
175
+ offset = 0,
176
+ date = null,
177
+ search = null
178
+ } = options;
179
+
180
+ const sessionsDir = getSessionsDir();
181
+
182
+ if (!fs.existsSync(sessionsDir)) {
183
+ return { sessions: [], total: 0, offset, limit, hasMore: false };
184
+ }
185
+
186
+ const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
187
+ const sessions = [];
188
+
189
+ for (const entry of entries) {
190
+ // Skip non-files (only process .tmp files)
191
+ if (!entry.isFile() || !entry.name.endsWith('.tmp')) continue;
192
+
193
+ const filename = entry.name;
194
+ const metadata = parseSessionFilename(filename);
195
+
196
+ if (!metadata) continue;
197
+
198
+ // Apply date filter
199
+ if (date && metadata.date !== date) {
200
+ continue;
201
+ }
202
+
203
+ // Apply search filter (search in short ID)
204
+ if (search && !metadata.shortId.includes(search)) {
205
+ continue;
206
+ }
207
+
208
+ const sessionPath = path.join(sessionsDir, filename);
209
+
210
+ // Get file stats
211
+ const stats = fs.statSync(sessionPath);
212
+
213
+ sessions.push({
214
+ ...metadata,
215
+ sessionPath,
216
+ hasContent: stats.size > 0,
217
+ size: stats.size,
218
+ modifiedTime: stats.mtime,
219
+ createdTime: stats.birthtime
220
+ });
221
+ }
222
+
223
+ // Sort by modified time (newest first)
224
+ sessions.sort((a, b) => b.modifiedTime - a.modifiedTime);
225
+
226
+ // Apply pagination
227
+ const paginatedSessions = sessions.slice(offset, offset + limit);
228
+
229
+ return {
230
+ sessions: paginatedSessions,
231
+ total: sessions.length,
232
+ offset,
233
+ limit,
234
+ hasMore: offset + limit < sessions.length
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Get a single session by ID (short ID or full path)
240
+ * @param {string} sessionId - Short ID or session filename
241
+ * @param {boolean} includeContent - Include session content
242
+ * @returns {object|null} Session object or null if not found
243
+ */
244
+ function getSessionById(sessionId, includeContent = false) {
245
+ const sessionsDir = getSessionsDir();
246
+
247
+ if (!fs.existsSync(sessionsDir)) {
248
+ return null;
249
+ }
250
+
251
+ const entries = fs.readdirSync(sessionsDir, { withFileTypes: true });
252
+
253
+ for (const entry of entries) {
254
+ if (!entry.isFile() || !entry.name.endsWith('.tmp')) continue;
255
+
256
+ const filename = entry.name;
257
+ const metadata = parseSessionFilename(filename);
258
+
259
+ if (!metadata) continue;
260
+
261
+ // Check if session ID matches (short ID or full filename without .tmp)
262
+ const shortIdMatch = metadata.shortId !== 'no-id' && metadata.shortId.startsWith(sessionId);
263
+ const filenameMatch = filename === sessionId || filename === `${sessionId}.tmp`;
264
+ const noIdMatch = metadata.shortId === 'no-id' && filename === `${sessionId}-session.tmp`;
265
+
266
+ if (!shortIdMatch && !filenameMatch && !noIdMatch) {
267
+ continue;
268
+ }
269
+
270
+ const sessionPath = path.join(sessionsDir, filename);
271
+ const stats = fs.statSync(sessionPath);
272
+
273
+ const session = {
274
+ ...metadata,
275
+ sessionPath,
276
+ size: stats.size,
277
+ modifiedTime: stats.mtime,
278
+ createdTime: stats.birthtime
279
+ };
280
+
281
+ if (includeContent) {
282
+ session.content = getSessionContent(sessionPath);
283
+ session.metadata = parseSessionMetadata(session.content);
284
+ session.stats = getSessionStats(sessionPath);
285
+ }
286
+
287
+ return session;
288
+ }
289
+
290
+ return null;
291
+ }
292
+
293
+ /**
294
+ * Get session title from content
295
+ * @param {string} sessionPath - Full path to session file
296
+ * @returns {string} Title or default text
297
+ */
298
+ function getSessionTitle(sessionPath) {
299
+ const content = getSessionContent(sessionPath);
300
+ const metadata = parseSessionMetadata(content);
301
+
302
+ return metadata.title || 'Untitled Session';
303
+ }
304
+
305
+ /**
306
+ * Format session size in human-readable format
307
+ * @param {string} sessionPath - Full path to session file
308
+ * @returns {string} Formatted size (e.g., "1.2 KB")
309
+ */
310
+ function getSessionSize(sessionPath) {
311
+ if (!fs.existsSync(sessionPath)) {
312
+ return '0 B';
313
+ }
314
+
315
+ const stats = fs.statSync(sessionPath);
316
+ const size = stats.size;
317
+
318
+ if (size < 1024) return `${size} B`;
319
+ if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)} KB`;
320
+ return `${(size / (1024 * 1024)).toFixed(1)} MB`;
321
+ }
322
+
323
+ /**
324
+ * Write session content to file
325
+ * @param {string} sessionPath - Full path to session file
326
+ * @param {string} content - Markdown content to write
327
+ * @returns {boolean} Success status
328
+ */
329
+ function writeSessionContent(sessionPath, content) {
330
+ try {
331
+ fs.writeFileSync(sessionPath, content, 'utf8');
332
+ return true;
333
+ } catch (err) {
334
+ log(`[SessionManager] Error writing session: ${err.message}`);
335
+ return false;
336
+ }
337
+ }
338
+
339
+ /**
340
+ * Append content to a session
341
+ * @param {string} sessionPath - Full path to session file
342
+ * @param {string} content - Content to append
343
+ * @returns {boolean} Success status
344
+ */
345
+ function appendSessionContent(sessionPath, content) {
346
+ try {
347
+ fs.appendFileSync(sessionPath, content, 'utf8');
348
+ return true;
349
+ } catch (err) {
350
+ log(`[SessionManager] Error appending to session: ${err.message}`);
351
+ return false;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Delete a session file
357
+ * @param {string} sessionPath - Full path to session file
358
+ * @returns {boolean} Success status
359
+ */
360
+ function deleteSession(sessionPath) {
361
+ try {
362
+ if (fs.existsSync(sessionPath)) {
363
+ fs.unlinkSync(sessionPath);
364
+ return true;
365
+ }
366
+ return false;
367
+ } catch (err) {
368
+ log(`[SessionManager] Error deleting session: ${err.message}`);
369
+ return false;
370
+ }
371
+ }
372
+
373
+ /**
374
+ * Check if a session exists
375
+ * @param {string} sessionPath - Full path to session file
376
+ * @returns {boolean} True if session exists
377
+ */
378
+ function sessionExists(sessionPath) {
379
+ return fs.existsSync(sessionPath) && fs.statSync(sessionPath).isFile();
380
+ }
381
+
382
+ module.exports = {
383
+ parseSessionFilename,
384
+ getSessionPath,
385
+ getSessionContent,
386
+ parseSessionMetadata,
387
+ getSessionStats,
388
+ getSessionTitle,
389
+ getSessionSize,
390
+ getAllSessions,
391
+ getSessionById,
392
+ writeSessionContent,
393
+ appendSessionContent,
394
+ deleteSession,
395
+ sessionExists
396
+ };