@claudemini/shit-cli 1.1.0 → 1.1.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.
package/lib/report.js CHANGED
@@ -3,7 +3,8 @@
3
3
  /**
4
4
  * Report generation module.
5
5
  * Produces summary.json (v2, bot-readable), summary.txt (human-readable),
6
- * prompts.txt, and metadata.json from session state + semantic data.
6
+ * prompts.txt, context.md, and metadata.json from session state + semantic data.
7
+ * Reference: Entire CLI's checkpoint format
7
8
  */
8
9
 
9
10
  import { writeFileSync } from 'fs';
@@ -21,6 +22,7 @@ export function generateReports(sessionDir, sessionId, state, intent, changes, c
21
22
  writeSummaryTxt(sessionDir, sessionId, state, intent, changes, classification, durationMin);
22
23
  writePromptsTxt(sessionDir, state);
23
24
  writeMetadataJson(sessionDir, sessionId, state, intent, classification, durationMin);
25
+ writeContextMd(sessionDir, sessionId, state, intent, changes, durationMin);
24
26
  }
25
27
 
26
28
  function writeSummaryJson(sessionDir, sessionId, state, intent, changes, classification, durationMin) {
@@ -61,7 +63,9 @@ function writeSummaryJson(sessionDir, sessionId, state, intent, changes, classif
61
63
  config_changed: classification.reviewHints.configChanged,
62
64
  migration_added: classification.reviewHints.migrationAdded,
63
65
  },
64
- prompts: state.prompts.map(p => typeof p === 'string' ? p : p.text),
66
+ prompts: state.prompts.map(p => typeof p === 'string' ? p : { time: p.time, text: p.text, type: p.type }),
67
+ transcript: state.transcript_path,
68
+ model: state.model,
65
69
  scope: intent.scope,
66
70
  };
67
71
  writeFileSync(join(sessionDir, 'summary.json'), JSON.stringify(summary, null, 2));
@@ -181,5 +185,112 @@ function writeMetadataJson(sessionDir, sessionId, state, intent, classification,
181
185
  errors: state.errors.length,
182
186
  scope: intent.scope,
183
187
  last_hook_type: state.last_hook_type,
188
+ // Enhanced metadata for Entire-style tracking
189
+ transcript_path: state.transcript_path || null,
190
+ model: state.model || null,
191
+ cwd: state.cwd || null,
184
192
  }, null, 2));
185
193
  }
194
+
195
+ /**
196
+ * Write context.md - human-readable session context (Entire-style)
197
+ */
198
+ function writeContextMd(sessionDir, sessionId, state, intent, changes, durationMin) {
199
+ const lines = [];
200
+
201
+ // Header
202
+ lines.push(`# Session Context: ${sessionId}`);
203
+ lines.push('');
204
+ lines.push(`**Started**: ${state.start_time ? new Date(state.start_time).toLocaleString() : 'unknown'}`);
205
+ lines.push(`**Ended**: ${state.last_time ? new Date(state.last_time).toLocaleString() : 'in progress'}`);
206
+ lines.push(`**Duration**: ${durationMin} minutes`);
207
+ lines.push(`**Model**: ${state.model || 'unknown'}`);
208
+ lines.push(`**Working Directory**: ${state.cwd || 'unknown'}`);
209
+ lines.push(`**Events**: ${state.event_count}`);
210
+ lines.push('');
211
+
212
+ // Transcript reference (Entire-style)
213
+ if (state.transcript_path) {
214
+ lines.push('## Transcript');
215
+ lines.push(`Full transcript available at: \`${state.transcript_path}\``);
216
+ lines.push('');
217
+ }
218
+
219
+ // User Prompts
220
+ if (state.prompts && state.prompts.length > 0) {
221
+ lines.push('## User Prompts');
222
+ lines.push('');
223
+ state.prompts.forEach((p, i) => {
224
+ const time = p.time ? new Date(p.time).toLocaleString() : '';
225
+ const text = typeof p === 'string' ? p : p.text;
226
+ const type = typeof p === 'object' && p.type ? ` [${p.type}]` : '';
227
+
228
+ lines.push(`### Prompt ${i + 1}${type}${time ? ` - ${time}` : ''}`);
229
+ lines.push('');
230
+ lines.push('```');
231
+ lines.push(text.slice(0, 2000));
232
+ lines.push('```');
233
+ lines.push('');
234
+ });
235
+ }
236
+
237
+ // Intent & Scope
238
+ if (intent.goal) {
239
+ lines.push('## Intent');
240
+ lines.push('');
241
+ lines.push(intent.goal);
242
+ lines.push('');
243
+ }
244
+
245
+ if (intent.scope && intent.scope.length > 0) {
246
+ lines.push('## Scope');
247
+ lines.push('');
248
+ intent.scope.forEach(s => lines.push(`- ${s}`));
249
+ lines.push('');
250
+ }
251
+
252
+ // Changes Summary
253
+ const modified = changes.files.filter(f => f.operations.some(op => op !== 'read'));
254
+ if (modified.length > 0) {
255
+ lines.push('## Files Changed');
256
+ lines.push('');
257
+ modified.slice(0, 30).forEach(f => {
258
+ const ops = f.operations.filter(op => op !== 'read').join(', ');
259
+ lines.push(`- \`${f.path}\` (${ops})`);
260
+ });
261
+ if (modified.length > 30) {
262
+ lines.push(`- ... and ${modified.length - 30} more files`);
263
+ }
264
+ lines.push('');
265
+ }
266
+
267
+ // Tool Usage
268
+ if (Object.keys(state.tool_counts).length > 0) {
269
+ lines.push('## Tool Usage');
270
+ lines.push('');
271
+ Object.entries(state.tool_counts)
272
+ .sort((a, b) => b[1] - a[1])
273
+ .forEach(([tool, count]) => {
274
+ lines.push(`- ${tool}: ${count}`);
275
+ });
276
+ lines.push('');
277
+ }
278
+
279
+ // Errors
280
+ if (state.errors.length > 0) {
281
+ lines.push('## Errors');
282
+ lines.push('');
283
+ state.errors.slice(0, 10).forEach(e => {
284
+ lines.push(`- **[${e.tool}]** ${(e.message || '').slice(0, 200)}`);
285
+ });
286
+ lines.push('');
287
+ }
288
+
289
+ // Footer
290
+ lines.push('---');
291
+ lines.push('');
292
+ lines.push(`*Generated by shit-cli at ${new Date().toISOString()}*`);
293
+ lines.push(`*See \`events.jsonl\` for complete event history*`);
294
+
295
+ writeFileSync(join(sessionDir, 'context.md'), lines.join('\n') + '\n');
296
+ }
package/lib/session.js CHANGED
@@ -22,6 +22,10 @@ const EMPTY_STATE = {
22
22
  prompts: [],
23
23
  last_hook_type: null,
24
24
  shadow_branch: null,
25
+ // Enhanced fields for Entire-style tracking
26
+ transcript_path: null,
27
+ cwd: null,
28
+ model: null,
25
29
  };
26
30
 
27
31
  export function loadState(sessionDir) {
@@ -63,10 +67,41 @@ export function processEvent(state, event, hookType, projectRoot) {
63
67
  const toolName = event.tool_name;
64
68
  const input = event.tool_input || {};
65
69
 
66
- // User prompts
67
- if (hookType === 'user-prompt-submit') {
68
- const prompt = event.prompt || event.message || '';
69
- if (prompt) state.prompts.push({ time: now, text: prompt });
70
+ // User prompts - support both camelCase and kebab-case
71
+ if (hookType === 'user-prompt-submit' || hookType === 'UserPromptSubmit') {
72
+ // Extract prompt from various possible fields
73
+ let prompt = '';
74
+ if (event.prompt) {
75
+ prompt = typeof event.prompt === 'string' ? event.prompt :
76
+ event.prompt.text || event.prompt.message || JSON.stringify(event.prompt).slice(0, 500);
77
+ } else if (event.message) {
78
+ prompt = typeof event.message === 'string' ? event.message :
79
+ event.message.text || JSON.stringify(event.message).slice(0, 500);
80
+ } else if (event.text) {
81
+ prompt = event.text;
82
+ }
83
+
84
+ if (prompt) {
85
+ state.prompts.push({
86
+ time: now,
87
+ text: prompt.slice(0, 2000), // Limit prompt length
88
+ type: event.prompt_type || 'user'
89
+ });
90
+ }
91
+ return;
92
+ }
93
+
94
+ // Record transcript path from SessionStart
95
+ if (hookType === 'session-start' || hookType === 'SessionStart') {
96
+ if (event.transcript_path) {
97
+ state.transcript_path = event.transcript_path;
98
+ }
99
+ if (event.cwd) {
100
+ state.cwd = event.cwd;
101
+ }
102
+ if (event.model) {
103
+ state.model = event.model;
104
+ }
70
105
  return;
71
106
  }
72
107
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@claudemini/shit-cli",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "Session-based Hook Intelligence Tracker - CLI tool for logging Claude Code hooks",
5
5
  "type": "module",
6
6
  "bin": {