@codeledger/cli 0.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.
Files changed (94) hide show
  1. package/LICENSE +27 -0
  2. package/dist/commands/activate.d.ts +18 -0
  3. package/dist/commands/activate.d.ts.map +1 -0
  4. package/dist/commands/activate.js +326 -0
  5. package/dist/commands/activate.js.map +1 -0
  6. package/dist/commands/bundle.d.ts +2 -0
  7. package/dist/commands/bundle.d.ts.map +1 -0
  8. package/dist/commands/bundle.js +144 -0
  9. package/dist/commands/bundle.js.map +1 -0
  10. package/dist/commands/clean.d.ts +2 -0
  11. package/dist/commands/clean.d.ts.map +1 -0
  12. package/dist/commands/clean.js +28 -0
  13. package/dist/commands/clean.js.map +1 -0
  14. package/dist/commands/compare.d.ts +2 -0
  15. package/dist/commands/compare.d.ts.map +1 -0
  16. package/dist/commands/compare.js +216 -0
  17. package/dist/commands/compare.js.map +1 -0
  18. package/dist/commands/init.d.ts +2 -0
  19. package/dist/commands/init.d.ts.map +1 -0
  20. package/dist/commands/init.js +135 -0
  21. package/dist/commands/init.js.map +1 -0
  22. package/dist/commands/run.d.ts +2 -0
  23. package/dist/commands/run.d.ts.map +1 -0
  24. package/dist/commands/run.js +57 -0
  25. package/dist/commands/run.js.map +1 -0
  26. package/dist/commands/scan.d.ts +2 -0
  27. package/dist/commands/scan.d.ts.map +1 -0
  28. package/dist/commands/scan.js +27 -0
  29. package/dist/commands/scan.js.map +1 -0
  30. package/dist/commands/session-cleanup.d.ts +11 -0
  31. package/dist/commands/session-cleanup.d.ts.map +1 -0
  32. package/dist/commands/session-cleanup.js +81 -0
  33. package/dist/commands/session-cleanup.js.map +1 -0
  34. package/dist/commands/session-init.d.ts +10 -0
  35. package/dist/commands/session-init.d.ts.map +1 -0
  36. package/dist/commands/session-init.js +46 -0
  37. package/dist/commands/session-init.js.map +1 -0
  38. package/dist/commands/session-progress.d.ts +12 -0
  39. package/dist/commands/session-progress.d.ts.map +1 -0
  40. package/dist/commands/session-progress.js +259 -0
  41. package/dist/commands/session-progress.js.map +1 -0
  42. package/dist/commands/session-summary.d.ts +14 -0
  43. package/dist/commands/session-summary.d.ts.map +1 -0
  44. package/dist/commands/session-summary.js +287 -0
  45. package/dist/commands/session-summary.js.map +1 -0
  46. package/dist/commands/sessions.d.ts +9 -0
  47. package/dist/commands/sessions.d.ts.map +1 -0
  48. package/dist/commands/sessions.js +60 -0
  49. package/dist/commands/sessions.js.map +1 -0
  50. package/dist/commands/share.d.ts +9 -0
  51. package/dist/commands/share.d.ts.map +1 -0
  52. package/dist/commands/share.js +275 -0
  53. package/dist/commands/share.js.map +1 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.d.ts.map +1 -0
  56. package/dist/index.js +150 -0
  57. package/dist/index.js.map +1 -0
  58. package/dist/session-id.d.ts +22 -0
  59. package/dist/session-id.d.ts.map +1 -0
  60. package/dist/session-id.js +49 -0
  61. package/dist/session-id.js.map +1 -0
  62. package/dist/session-paths.d.ts +23 -0
  63. package/dist/session-paths.d.ts.map +1 -0
  64. package/dist/session-paths.js +44 -0
  65. package/dist/session-paths.js.map +1 -0
  66. package/dist/session-registry.d.ts +19 -0
  67. package/dist/session-registry.d.ts.map +1 -0
  68. package/dist/session-registry.js +180 -0
  69. package/dist/session-registry.js.map +1 -0
  70. package/dist/templates/claude-md.d.ts +10 -0
  71. package/dist/templates/claude-md.d.ts.map +1 -0
  72. package/dist/templates/claude-md.js +107 -0
  73. package/dist/templates/claude-md.js.map +1 -0
  74. package/dist/templates/config.d.ts +3 -0
  75. package/dist/templates/config.d.ts.map +1 -0
  76. package/dist/templates/config.js +82 -0
  77. package/dist/templates/config.js.map +1 -0
  78. package/dist/templates/harness-pack.d.ts +3 -0
  79. package/dist/templates/harness-pack.d.ts.map +1 -0
  80. package/dist/templates/harness-pack.js +26 -0
  81. package/dist/templates/harness-pack.js.map +1 -0
  82. package/dist/templates/hooks.d.ts +34 -0
  83. package/dist/templates/hooks.d.ts.map +1 -0
  84. package/dist/templates/hooks.js +149 -0
  85. package/dist/templates/hooks.js.map +1 -0
  86. package/dist/templates/scenarios.d.ts +3 -0
  87. package/dist/templates/scenarios.d.ts.map +1 -0
  88. package/dist/templates/scenarios.js +335 -0
  89. package/dist/templates/scenarios.js.map +1 -0
  90. package/dist/version.d.ts +2 -0
  91. package/dist/version.d.ts.map +1 -0
  92. package/dist/version.js +5 -0
  93. package/dist/version.js.map +1 -0
  94. package/package.json +45 -0
@@ -0,0 +1,46 @@
1
+ import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { generateSessionId } from '../session-id.js';
4
+ import { sessionDir } from '../session-paths.js';
5
+ import { registerSession } from '../session-registry.js';
6
+ /**
7
+ * `codeledger session-init`
8
+ *
9
+ * Lightweight command to establish a new session identity.
10
+ * Called from the SessionStart hook to generate a session ID, create
11
+ * the session directory, and register the session. Prints the session
12
+ * ID to stdout for capture in the hook script.
13
+ */
14
+ export async function runSessionInit(cwd, flags) {
15
+ const quiet = flags['quiet'] === 'true';
16
+ const sessionId = generateSessionId();
17
+ // Create the session directory
18
+ const dir = sessionDir(cwd, sessionId);
19
+ if (!existsSync(dir)) {
20
+ mkdirSync(dir, { recursive: true });
21
+ }
22
+ // Register the session
23
+ const now = new Date().toISOString();
24
+ registerSession(cwd, {
25
+ session_id: sessionId,
26
+ task: null,
27
+ started_at: now,
28
+ last_active_at: now,
29
+ pid: process.ppid ?? process.pid,
30
+ bundle_files: [],
31
+ status: 'active',
32
+ });
33
+ // Write PID-tagged marker so downstream hooks can discover this session
34
+ const ppid = process.ppid ?? process.pid;
35
+ const markerPath = join(cwd, '.codeledger', `.current-session-pid-${ppid}`);
36
+ const markerDir = join(cwd, '.codeledger');
37
+ if (!existsSync(markerDir))
38
+ mkdirSync(markerDir, { recursive: true });
39
+ writeFileSync(markerPath, sessionId + '\n');
40
+ // Print session ID to stdout (captured by hook script)
41
+ console.log(sessionId);
42
+ if (!quiet) {
43
+ console.error(`CodeLedger: session ${sessionId} initialized`);
44
+ }
45
+ }
46
+ //# sourceMappingURL=session-init.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-init.js","sourceRoot":"","sources":["../../src/commands/session-init.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACjD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAC;AAEzD;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,cAAc,CAClC,GAAW,EACX,KAA6B;IAE7B,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,KAAK,MAAM,CAAC;IACxC,MAAM,SAAS,GAAG,iBAAiB,EAAE,CAAC;IAEtC,+BAA+B;IAC/B,MAAM,GAAG,GAAG,UAAU,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IAED,uBAAuB;IACvB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IACrC,eAAe,CAAC,GAAG,EAAE;QACnB,UAAU,EAAE,SAAS;QACrB,IAAI,EAAE,IAAI;QACV,UAAU,EAAE,GAAG;QACf,cAAc,EAAE,GAAG;QACnB,GAAG,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG;QAChC,YAAY,EAAE,EAAE;QAChB,MAAM,EAAE,QAAQ;KACjB,CAAC,CAAC;IAEH,wEAAwE;IACxE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC;IACzC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,EAAE,wBAAwB,IAAI,EAAE,CAAC,CAAC;IAC5E,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC3C,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC;QAAE,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtE,aAAa,CAAC,UAAU,EAAE,SAAS,GAAG,IAAI,CAAC,CAAC;IAE5C,uDAAuD;IACvD,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;IAEvB,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,OAAO,CAAC,KAAK,CAAC,uBAAuB,SAAS,cAAc,CAAC,CAAC;IAChE,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `codeledger session-progress [--session <id>]`
3
+ *
4
+ * Writes a session-progress.md — a ground-truth snapshot of implementation
5
+ * state derived from git. Designed to be called from the PreCompact hook
6
+ * so the progress file survives context compaction.
7
+ *
8
+ * Multi-session: when a session ID is provided, reads/writes from the
9
+ * session-specific directory instead of the shared .codeledger/ root.
10
+ */
11
+ export declare function runSessionProgress(cwd: string, flags: Record<string, string>): Promise<void>;
12
+ //# sourceMappingURL=session-progress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-progress.d.ts","sourceRoot":"","sources":["../../src/commands/session-progress.ts"],"names":[],"mappings":"AAOA;;;;;;;;;GASG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAwCf"}
@@ -0,0 +1,259 @@
1
+ import { readFileSync, writeFileSync, existsSync, readdirSync, statSync, mkdirSync } from 'node:fs';
2
+ import { join, dirname } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { resolveSessionId } from '../session-id.js';
5
+ import { sessionStartRef, sessionProgressPath } from '../session-paths.js';
6
+ /**
7
+ * `codeledger session-progress [--session <id>]`
8
+ *
9
+ * Writes a session-progress.md — a ground-truth snapshot of implementation
10
+ * state derived from git. Designed to be called from the PreCompact hook
11
+ * so the progress file survives context compaction.
12
+ *
13
+ * Multi-session: when a session ID is provided, reads/writes from the
14
+ * session-specific directory instead of the shared .codeledger/ root.
15
+ */
16
+ export async function runSessionProgress(cwd, flags) {
17
+ const configPath = join(cwd, '.codeledger', 'config.json');
18
+ if (!existsSync(configPath))
19
+ return;
20
+ const quiet = flags['quiet'] === 'true';
21
+ const config = JSON.parse(readFileSync(configPath, 'utf-8'));
22
+ const sessionId = resolveSessionId({ session: flags['session'] });
23
+ const bundle = loadLatestBundle(cwd, config);
24
+ const startRef = loadSessionStartRef(cwd, sessionId);
25
+ const commits = getSessionCommits(cwd, startRef);
26
+ const uncommitted = getUncommittedChanges(cwd);
27
+ const allChanged = getAllChangedFiles(cwd, startRef);
28
+ // Cross-reference bundle files against actual changes
29
+ const bundleFiles = bundle ? bundle.files : [];
30
+ const changedSet = new Set(allChanged);
31
+ const untouched = bundleFiles.filter((f) => !changedSet.has(f.path));
32
+ const md = renderProgressMarkdown({
33
+ task: bundle?.task ?? null,
34
+ bundleId: bundle?.bundle_id ?? null,
35
+ sessionId,
36
+ commits,
37
+ uncommitted,
38
+ allChanged,
39
+ untouched,
40
+ });
41
+ const progressPath = sessionProgressPath(cwd, sessionId);
42
+ const dir = dirname(progressPath);
43
+ if (!existsSync(dir))
44
+ mkdirSync(dir, { recursive: true });
45
+ writeFileSync(progressPath, md);
46
+ if (!quiet) {
47
+ const displayPath = sessionId
48
+ ? `.codeledger/sessions/${sessionId}/session-progress.md`
49
+ : '.codeledger/session-progress.md';
50
+ console.log(`CodeLedger: session progress written to ${displayPath}`);
51
+ }
52
+ }
53
+ // ── Git introspection ──────────────────────────────────────────────────────
54
+ function loadSessionStartRef(cwd, sessionId) {
55
+ const refPath = sessionStartRef(cwd, sessionId);
56
+ if (!existsSync(refPath))
57
+ return null;
58
+ const ref = readFileSync(refPath, 'utf-8').trim();
59
+ return ref || null;
60
+ }
61
+ function getSessionCommits(cwd, startRef) {
62
+ if (!startRef)
63
+ return [];
64
+ try {
65
+ const log = execSync(`git log --format="%H %s" ${startRef}..HEAD`, { cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
66
+ if (!log)
67
+ return [];
68
+ return log.split('\n').map((line) => {
69
+ const spaceIdx = line.indexOf(' ');
70
+ const sha = line.slice(0, spaceIdx);
71
+ const message = line.slice(spaceIdx + 1);
72
+ const files = getCommitFiles(cwd, sha);
73
+ return { sha: sha.slice(0, 7), message, files };
74
+ });
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ }
80
+ function getCommitFiles(cwd, sha) {
81
+ try {
82
+ const output = execSync(`git diff-tree --no-commit-id --name-only -r ${sha}`, { cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
83
+ return output ? output.split('\n') : [];
84
+ }
85
+ catch {
86
+ return [];
87
+ }
88
+ }
89
+ function getUncommittedChanges(cwd) {
90
+ const changes = [];
91
+ try {
92
+ // Staged + unstaged modifications
93
+ const diff = execSync('git diff --name-status HEAD', {
94
+ cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
95
+ }).trim();
96
+ if (diff) {
97
+ for (const line of diff.split('\n')) {
98
+ const [status, ...rest] = line.split('\t');
99
+ if (status && rest.length > 0) {
100
+ changes.push({ status: status.charAt(0), path: rest.join('\t') });
101
+ }
102
+ }
103
+ }
104
+ // Untracked files
105
+ const untracked = execSync('git ls-files --others --exclude-standard', {
106
+ cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
107
+ }).trim();
108
+ if (untracked) {
109
+ for (const f of untracked.split('\n')) {
110
+ changes.push({ status: '?', path: f });
111
+ }
112
+ }
113
+ }
114
+ catch {
115
+ // Non-fatal
116
+ }
117
+ return changes;
118
+ }
119
+ function getAllChangedFiles(cwd, startRef) {
120
+ const files = new Set();
121
+ try {
122
+ // Uncommitted
123
+ const diff = execSync('git diff --name-only HEAD', {
124
+ cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
125
+ }).trim();
126
+ if (diff)
127
+ for (const f of diff.split('\n'))
128
+ files.add(f);
129
+ // Untracked
130
+ const untracked = execSync('git ls-files --others --exclude-standard', {
131
+ cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'],
132
+ }).trim();
133
+ if (untracked)
134
+ for (const f of untracked.split('\n'))
135
+ files.add(f);
136
+ // Committed since session start
137
+ if (startRef) {
138
+ try {
139
+ const committed = execSync(`git diff --name-only ${startRef}..HEAD`, { cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
140
+ if (committed)
141
+ for (const f of committed.split('\n'))
142
+ files.add(f);
143
+ }
144
+ catch {
145
+ // startRef may be invalid
146
+ }
147
+ }
148
+ }
149
+ catch {
150
+ // Non-fatal
151
+ }
152
+ return [...files];
153
+ }
154
+ // ── Bundle loading (mirrors session-summary.ts) ────────────────────────────
155
+ function loadLatestBundle(cwd, config) {
156
+ const bundleDir = join(cwd, config.workspace.artifacts_dir, 'bundles');
157
+ if (!existsSync(bundleDir))
158
+ return null;
159
+ const files = readdirSync(bundleDir)
160
+ .filter((f) => f.endsWith('.json'))
161
+ .map((f) => ({
162
+ name: f,
163
+ mtime: statSync(join(bundleDir, f)).mtimeMs,
164
+ }))
165
+ .sort((a, b) => b.mtime - a.mtime);
166
+ if (files.length === 0)
167
+ return null;
168
+ const latestPath = join(bundleDir, files[0].name);
169
+ return JSON.parse(readFileSync(latestPath, 'utf-8'));
170
+ }
171
+ // ── Markdown rendering ─────────────────────────────────────────────────────
172
+ function renderProgressMarkdown(data) {
173
+ const lines = [];
174
+ lines.push('# CodeLedger Session Progress');
175
+ lines.push('');
176
+ lines.push('<!-- Auto-generated by codeledger session-progress. -->');
177
+ lines.push('<!-- Read this after context compaction to understand what has been accomplished. -->');
178
+ lines.push('');
179
+ lines.push(`Updated: ${new Date().toISOString()}`);
180
+ if (data.sessionId) {
181
+ lines.push(`Session: ${data.sessionId}`);
182
+ }
183
+ lines.push('');
184
+ // ── Task ──
185
+ if (data.task) {
186
+ lines.push('## Task');
187
+ lines.push('');
188
+ lines.push(data.task);
189
+ lines.push('');
190
+ }
191
+ // ── Commits ──
192
+ if (data.commits.length > 0) {
193
+ lines.push('## Commits This Session');
194
+ lines.push('');
195
+ for (const c of data.commits) {
196
+ lines.push(`- \`${c.sha}\` ${c.message}`);
197
+ for (const f of c.files) {
198
+ lines.push(` - ${f}`);
199
+ }
200
+ }
201
+ lines.push('');
202
+ }
203
+ else {
204
+ lines.push('## Commits This Session');
205
+ lines.push('');
206
+ lines.push('No commits yet.');
207
+ lines.push('');
208
+ }
209
+ // ── Uncommitted changes ──
210
+ if (data.uncommitted.length > 0) {
211
+ lines.push('## Uncommitted Changes');
212
+ lines.push('');
213
+ for (const c of data.uncommitted) {
214
+ const label = statusLabel(c.status);
215
+ lines.push(`- ${c.path} (${label})`);
216
+ }
217
+ lines.push('');
218
+ }
219
+ // ── Remaining bundle files ──
220
+ if (data.untouched.length > 0) {
221
+ lines.push('## Bundle Files Not Yet Touched');
222
+ lines.push('');
223
+ lines.push('These files were selected as relevant but have not been modified:');
224
+ lines.push('');
225
+ for (const f of data.untouched.slice(0, 15)) {
226
+ const reasons = f.reasons.join(', ');
227
+ lines.push(`- ${f.path} (score: ${f.score.toFixed(2)}, ${reasons})`);
228
+ }
229
+ if (data.untouched.length > 15) {
230
+ lines.push(`- ... and ${data.untouched.length - 15} more`);
231
+ }
232
+ lines.push('');
233
+ }
234
+ // ── Summary line ──
235
+ lines.push('## At a Glance');
236
+ lines.push('');
237
+ lines.push(`- ${data.commits.length} commit(s)`);
238
+ lines.push(`- ${data.allChanged.length} file(s) changed`);
239
+ lines.push(`- ${data.uncommitted.length} uncommitted change(s)`);
240
+ lines.push(`- ${data.untouched.length} bundle file(s) not yet touched`);
241
+ if (data.bundleId) {
242
+ lines.push(`- Bundle: ${data.bundleId}`);
243
+ }
244
+ if (data.sessionId) {
245
+ lines.push(`- Session: ${data.sessionId}`);
246
+ }
247
+ lines.push('');
248
+ return lines.join('\n');
249
+ }
250
+ function statusLabel(status) {
251
+ switch (status) {
252
+ case 'M': return 'modified';
253
+ case 'A': return 'added';
254
+ case 'D': return 'deleted';
255
+ case '?': return 'new file';
256
+ default: return status;
257
+ }
258
+ }
259
+ //# sourceMappingURL=session-progress.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-progress.js","sourceRoot":"","sources":["../../src/commands/session-progress.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * `codeledger session-summary [--session <id>]`
3
+ *
4
+ * Lightweight session-end value report. Compares the active context bundle
5
+ * against files actually changed during the session, producing a concise
6
+ * recap that reinforces the value of deterministic context selection.
7
+ *
8
+ * Designed to be called from the Claude Code Stop hook.
9
+ *
10
+ * Multi-session: when a session ID is provided, reads refs from the
11
+ * session-specific directory instead of the shared .codeledger/ root.
12
+ */
13
+ export declare function runSessionSummary(cwd: string, flags: Record<string, string>): Promise<void>;
14
+ //# sourceMappingURL=session-summary.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-summary.d.ts","sourceRoot":"","sources":["../../src/commands/session-summary.ts"],"names":[],"mappings":"AAOA;;;;;;;;;;;GAWG;AACH,wBAAsB,iBAAiB,CACrC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC5B,OAAO,CAAC,IAAI,CAAC,CAsEf"}
@@ -0,0 +1,287 @@
1
+ import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { execSync } from 'node:child_process';
4
+ import { resolveSessionId } from '../session-id.js';
5
+ import { sessionActivateRef, sessionStartRef } from '../session-paths.js';
6
+ /**
7
+ * `codeledger session-summary [--session <id>]`
8
+ *
9
+ * Lightweight session-end value report. Compares the active context bundle
10
+ * against files actually changed during the session, producing a concise
11
+ * recap that reinforces the value of deterministic context selection.
12
+ *
13
+ * Designed to be called from the Claude Code Stop hook.
14
+ *
15
+ * Multi-session: when a session ID is provided, reads refs from the
16
+ * session-specific directory instead of the shared .codeledger/ root.
17
+ */
18
+ export async function runSessionSummary(cwd, flags) {
19
+ const configPath = join(cwd, '.codeledger', 'config.json');
20
+ if (!existsSync(configPath)) {
21
+ // No CodeLedger setup — exit silently (don't pollute agent output)
22
+ return;
23
+ }
24
+ let config;
25
+ try {
26
+ config = JSON.parse(readFileSync(configPath, 'utf-8'));
27
+ }
28
+ catch {
29
+ // Malformed config — exit silently (don't pollute agent output from hook)
30
+ return;
31
+ }
32
+ const sessionId = resolveSessionId({ session: flags['session'] });
33
+ // ── Find the latest bundle artifact ─────────────────────────────────────
34
+ const bundle = loadLatestBundle(cwd, config);
35
+ if (!bundle) {
36
+ // No bundle was generated this session — nothing to summarize
37
+ return;
38
+ }
39
+ // ── Gather actual file changes from git ─────────────────────────────────
40
+ const filesChanged = getSessionChangedFiles(cwd, sessionId);
41
+ if (filesChanged.length === 0) {
42
+ console.log('CodeLedger: no files changed this session — nothing to recap.');
43
+ return;
44
+ }
45
+ // ── Compute overlap metrics ─────────────────────────────────────────────
46
+ const bundleFiles = bundle.files.map((f) => f.path);
47
+ const overlap = filesChanged.filter((f) => bundleFiles.includes(f));
48
+ const recall = filesChanged.length > 0
49
+ ? overlap.length / filesChanged.length
50
+ : 0;
51
+ const precision = bundleFiles.length > 0
52
+ ? overlap.length / bundleFiles.length
53
+ : 0;
54
+ // ── Estimate repo-wide token count for context reduction ────────────────
55
+ const repoTokens = estimateRepoTokens(cwd, config);
56
+ const reductionPct = repoTokens > 0
57
+ ? ((repoTokens - bundle.total_tokens) / repoTokens) * 100
58
+ : 0;
59
+ const summary = {
60
+ bundle_id: bundle.bundle_id,
61
+ task: bundle.task,
62
+ bundle_files: bundleFiles,
63
+ files_changed: filesChanged,
64
+ files_overlapping: overlap,
65
+ recall,
66
+ precision,
67
+ bundle_tokens: bundle.total_tokens,
68
+ repo_total_tokens: repoTokens,
69
+ context_reduction_pct: reductionPct,
70
+ generated_at: new Date().toISOString(),
71
+ session_id: sessionId ?? undefined,
72
+ };
73
+ // ── Print concise recap ─────────────────────────────────────────────────
74
+ printRecap(summary);
75
+ // ── Session-wide aggregate (only on --final, i.e. Stop hook) ───────────
76
+ if (flags['final'] === 'true') {
77
+ printSessionAggregate(cwd, sessionId, summary);
78
+ }
79
+ }
80
+ function loadLatestBundle(cwd, config) {
81
+ const bundleDir = join(cwd, config.workspace.artifacts_dir, 'bundles');
82
+ if (!existsSync(bundleDir))
83
+ return null;
84
+ const files = readdirSync(bundleDir)
85
+ .filter((f) => f.endsWith('.json'))
86
+ .map((f) => ({
87
+ name: f,
88
+ mtime: statSync(join(bundleDir, f)).mtimeMs,
89
+ }))
90
+ .sort((a, b) => b.mtime - a.mtime);
91
+ if (files.length === 0)
92
+ return null;
93
+ const latestPath = join(bundleDir, files[0].name);
94
+ try {
95
+ return JSON.parse(readFileSync(latestPath, 'utf-8'));
96
+ }
97
+ catch {
98
+ // Corrupted or deleted bundle file — treat as missing
99
+ return null;
100
+ }
101
+ }
102
+ function getSessionChangedFiles(cwd, sessionId) {
103
+ try {
104
+ const files = new Set();
105
+ // 1. Uncommitted changes (staged + unstaged)
106
+ const diffOutput = execSync('git diff --name-only HEAD', {
107
+ cwd,
108
+ encoding: 'utf-8',
109
+ timeout: 5000,
110
+ stdio: ['pipe', 'pipe', 'pipe'],
111
+ }).trim();
112
+ if (diffOutput) {
113
+ for (const f of diffOutput.split('\n'))
114
+ files.add(f);
115
+ }
116
+ // 2. Untracked files
117
+ const untrackedOutput = execSync('git ls-files --others --exclude-standard', {
118
+ cwd,
119
+ encoding: 'utf-8',
120
+ timeout: 5000,
121
+ stdio: ['pipe', 'pipe', 'pipe'],
122
+ }).trim();
123
+ if (untrackedOutput) {
124
+ for (const f of untrackedOutput.split('\n'))
125
+ files.add(f);
126
+ }
127
+ // 3. Committed changes: prefer .activate-ref (scoped to current task)
128
+ // over .session-start-ref (full session) for accurate per-task metrics
129
+ // Use session-specific paths when a session ID is available
130
+ const activateRefPath = sessionActivateRef(cwd, sessionId);
131
+ const sessionRefPath = sessionStartRef(cwd, sessionId);
132
+ const refPath = existsSync(activateRefPath) ? activateRefPath : sessionRefPath;
133
+ if (existsSync(refPath)) {
134
+ const startRef = readFileSync(refPath, 'utf-8').trim();
135
+ if (startRef) {
136
+ try {
137
+ const committedDiff = execSync(`git diff --name-only ${startRef}..HEAD`, { cwd, encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] }).trim();
138
+ if (committedDiff) {
139
+ for (const f of committedDiff.split('\n'))
140
+ files.add(f);
141
+ }
142
+ }
143
+ catch {
144
+ // startRef may no longer be valid (rebased away, etc.) — non-fatal
145
+ }
146
+ }
147
+ }
148
+ return [...files];
149
+ }
150
+ catch {
151
+ return [];
152
+ }
153
+ }
154
+ function estimateRepoTokens(cwd, config) {
155
+ const indexPath = join(cwd, config.workspace.cache_dir, 'repo-index.json');
156
+ if (!existsSync(indexPath))
157
+ return 0;
158
+ try {
159
+ const repoIndex = JSON.parse(readFileSync(indexPath, 'utf-8'));
160
+ // Use same heuristic as selector stop-rule: 4 tokens per line
161
+ return repoIndex.files.reduce((sum, f) => sum + f.lines * 4, 0);
162
+ }
163
+ catch {
164
+ return 0;
165
+ }
166
+ }
167
+ function printRecap(s) {
168
+ const recallPct = Math.round(s.recall * 100);
169
+ const precisionPct = Math.round(s.precision * 100);
170
+ const reductionPct = Math.round(s.context_reduction_pct);
171
+ // F1 score: harmonic mean of precision and recall — single usefulness metric
172
+ const f1 = (s.precision + s.recall) > 0
173
+ ? 2 * (s.precision * s.recall) / (s.precision + s.recall)
174
+ : 0;
175
+ const lines = [];
176
+ lines.push('');
177
+ lines.push('-- CodeLedger Session Recap ----------------------------------');
178
+ if (s.session_id) {
179
+ lines.push(` Session: ${s.session_id}`);
180
+ }
181
+ if (f1 < 0.3) {
182
+ // Bundle was not useful — lead with the miss, suppress reduction stat
183
+ lines.push(` Bundle missed: predicted ${s.files_overlapping.length}/${s.files_changed.length} files you changed (${recallPct}% recall)`);
184
+ lines.push(` Bundle accuracy: ${s.files_overlapping.length}/${s.bundle_files.length} bundled files were relevant (${precisionPct}% precision)`);
185
+ lines.push(` The bundle was not useful for this task.`);
186
+ lines.push(` Tip: Re-run "codeledger activate --task ..." with a more specific task description.`);
187
+ }
188
+ else if (s.recall < 0.5) {
189
+ // Bundle was partially useful — show all stats but highlight room to improve
190
+ lines.push(` Bundle predicted ${s.files_overlapping.length}/${s.files_changed.length} files you changed (${recallPct}% recall)`);
191
+ lines.push(` Bundle accuracy: ${s.files_overlapping.length}/${s.bundle_files.length} bundled files were relevant (${precisionPct}% precision)`);
192
+ lines.push(` Context: ~${fmtTokens(s.bundle_tokens)} tokens vs ~${fmtTokens(s.repo_total_tokens)} full repo (${reductionPct}% reduction)`);
193
+ lines.push(` Tip: A more specific task description may improve recall.`);
194
+ }
195
+ else {
196
+ // Bundle worked well — show the full value story
197
+ lines.push(` Bundle predicted ${s.files_overlapping.length}/${s.files_changed.length} files you changed (${recallPct}% recall)`);
198
+ lines.push(` Bundle accuracy: ${s.files_overlapping.length}/${s.bundle_files.length} bundled files were relevant (${precisionPct}% precision)`);
199
+ lines.push(` Context: ~${fmtTokens(s.bundle_tokens)} tokens vs ~${fmtTokens(s.repo_total_tokens)} full repo (${reductionPct}% reduction)`);
200
+ }
201
+ lines.push(` [${s.bundle_id}]`);
202
+ lines.push('-'.repeat(61));
203
+ lines.push('');
204
+ console.log(lines.join('\n'));
205
+ }
206
+ function fmtTokens(n) {
207
+ if (n >= 1000)
208
+ return `${(n / 1000).toFixed(1)}k`;
209
+ return `${n}`;
210
+ }
211
+ /**
212
+ * When the session involved multiple tasks (`.session-start-ref` differs from
213
+ * `.activate-ref`), print a session-wide aggregate line so the developer sees
214
+ * the full picture at session end.
215
+ */
216
+ function printSessionAggregate(cwd, sessionId, lastTaskSummary) {
217
+ const activateRefPath = sessionActivateRef(cwd, sessionId);
218
+ const sessionRefPath = sessionStartRef(cwd, sessionId);
219
+ // Both refs must exist and differ to indicate multiple tasks were run
220
+ if (!existsSync(sessionRefPath) || !existsSync(activateRefPath))
221
+ return;
222
+ const sessionRef = readFileSync(sessionRefPath, 'utf-8').trim();
223
+ const activateRef = readFileSync(activateRefPath, 'utf-8').trim();
224
+ if (!sessionRef || !activateRef || sessionRef === activateRef)
225
+ return;
226
+ // Gather all files changed since session start
227
+ const allChanged = new Set();
228
+ try {
229
+ const committed = execSync(`git diff --name-only ${sessionRef}..HEAD`, {
230
+ cwd,
231
+ encoding: 'utf-8',
232
+ timeout: 5000,
233
+ stdio: ['pipe', 'pipe', 'pipe'],
234
+ }).trim();
235
+ if (committed) {
236
+ for (const f of committed.split('\n'))
237
+ allChanged.add(f);
238
+ }
239
+ }
240
+ catch {
241
+ return;
242
+ }
243
+ // Include uncommitted work
244
+ try {
245
+ const uncommitted = execSync('git diff --name-only HEAD', {
246
+ cwd,
247
+ encoding: 'utf-8',
248
+ timeout: 5000,
249
+ stdio: ['pipe', 'pipe', 'pipe'],
250
+ }).trim();
251
+ if (uncommitted) {
252
+ for (const f of uncommitted.split('\n'))
253
+ allChanged.add(f);
254
+ }
255
+ }
256
+ catch {
257
+ // non-fatal
258
+ }
259
+ // Only show aggregate if it covers more files than the last task alone
260
+ if (allChanged.size <= lastTaskSummary.files_changed.length)
261
+ return;
262
+ // Count commits since session start
263
+ let commitCount = 0;
264
+ try {
265
+ const count = execSync(`git rev-list --count ${sessionRef}..HEAD`, {
266
+ cwd,
267
+ encoding: 'utf-8',
268
+ timeout: 5000,
269
+ stdio: ['pipe', 'pipe', 'pipe'],
270
+ }).trim();
271
+ commitCount = parseInt(count, 10) || 0;
272
+ }
273
+ catch {
274
+ // non-fatal
275
+ }
276
+ const lines = [];
277
+ lines.push('');
278
+ lines.push('-- Session Totals (all tasks) --------------------------------');
279
+ lines.push(` ${allChanged.size} files changed across ${commitCount} commit${commitCount === 1 ? '' : 's'} this session`);
280
+ if (sessionId) {
281
+ lines.push(` Session: ${sessionId}`);
282
+ }
283
+ lines.push('-'.repeat(61));
284
+ lines.push('');
285
+ console.log(lines.join('\n'));
286
+ }
287
+ //# sourceMappingURL=session-summary.js.map