@tyroneross/bookmark 0.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.
Files changed (79) hide show
  1. package/.claude-plugin/marketplace.json +15 -0
  2. package/.claude-plugin/plugin.json +13 -0
  3. package/CLAUDE.md +69 -0
  4. package/LICENSE +21 -0
  5. package/README.md +178 -0
  6. package/agents/snapshot-analyst.md +41 -0
  7. package/commands/activate.md +20 -0
  8. package/commands/list.md +15 -0
  9. package/commands/restore.md +30 -0
  10. package/commands/snapshot.md +26 -0
  11. package/commands/status.md +19 -0
  12. package/dist/cli/index.d.ts +3 -0
  13. package/dist/cli/index.d.ts.map +1 -0
  14. package/dist/cli/index.js +437 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/config.d.ts +5 -0
  17. package/dist/config.d.ts.map +1 -0
  18. package/dist/config.js +86 -0
  19. package/dist/config.js.map +1 -0
  20. package/dist/index.d.ts +13 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js +13 -0
  23. package/dist/index.js.map +1 -0
  24. package/dist/restore/index.d.ts +13 -0
  25. package/dist/restore/index.d.ts.map +1 -0
  26. package/dist/restore/index.js +94 -0
  27. package/dist/restore/index.js.map +1 -0
  28. package/dist/setup/auto-setup.d.ts +16 -0
  29. package/dist/setup/auto-setup.d.ts.map +1 -0
  30. package/dist/setup/auto-setup.js +192 -0
  31. package/dist/setup/auto-setup.js.map +1 -0
  32. package/dist/setup/configure-hooks.d.ts +10 -0
  33. package/dist/setup/configure-hooks.d.ts.map +1 -0
  34. package/dist/setup/configure-hooks.js +97 -0
  35. package/dist/setup/configure-hooks.js.map +1 -0
  36. package/dist/snapshot/capture.d.ts +21 -0
  37. package/dist/snapshot/capture.d.ts.map +1 -0
  38. package/dist/snapshot/capture.js +134 -0
  39. package/dist/snapshot/capture.js.map +1 -0
  40. package/dist/snapshot/compress.d.ts +8 -0
  41. package/dist/snapshot/compress.d.ts.map +1 -0
  42. package/dist/snapshot/compress.js +93 -0
  43. package/dist/snapshot/compress.js.map +1 -0
  44. package/dist/snapshot/storage.d.ts +13 -0
  45. package/dist/snapshot/storage.d.ts.map +1 -0
  46. package/dist/snapshot/storage.js +151 -0
  47. package/dist/snapshot/storage.js.map +1 -0
  48. package/dist/threshold/adaptive.d.ts +23 -0
  49. package/dist/threshold/adaptive.d.ts.map +1 -0
  50. package/dist/threshold/adaptive.js +29 -0
  51. package/dist/threshold/adaptive.js.map +1 -0
  52. package/dist/threshold/state.d.ts +8 -0
  53. package/dist/threshold/state.d.ts.map +1 -0
  54. package/dist/threshold/state.js +83 -0
  55. package/dist/threshold/state.js.map +1 -0
  56. package/dist/threshold/time-based.d.ts +13 -0
  57. package/dist/threshold/time-based.d.ts.map +1 -0
  58. package/dist/threshold/time-based.js +24 -0
  59. package/dist/threshold/time-based.js.map +1 -0
  60. package/dist/transcript/estimator.d.ts +20 -0
  61. package/dist/transcript/estimator.d.ts.map +1 -0
  62. package/dist/transcript/estimator.js +95 -0
  63. package/dist/transcript/estimator.js.map +1 -0
  64. package/dist/transcript/extractor.d.ts +7 -0
  65. package/dist/transcript/extractor.d.ts.map +1 -0
  66. package/dist/transcript/extractor.js +237 -0
  67. package/dist/transcript/extractor.js.map +1 -0
  68. package/dist/transcript/parser.d.ts +16 -0
  69. package/dist/transcript/parser.d.ts.map +1 -0
  70. package/dist/transcript/parser.js +120 -0
  71. package/dist/transcript/parser.js.map +1 -0
  72. package/dist/types.d.ts +156 -0
  73. package/dist/types.d.ts.map +1 -0
  74. package/dist/types.js +3 -0
  75. package/dist/types.js.map +1 -0
  76. package/hooks/hooks.json +54 -0
  77. package/package.json +77 -0
  78. package/scripts/install-plugin.sh +38 -0
  79. package/skills/context-continuity/SKILL.md +49 -0
@@ -0,0 +1,94 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readLatestMd, loadLatestSnapshot, getSnapshotCount } from '../snapshot/storage.js';
3
+ import { loadState, saveState, resetForNewSession, incrementCompaction } from '../threshold/state.js';
4
+ import { loadConfig, getStoragePath } from '../config.js';
5
+ /**
6
+ * Generate restoration context for a SessionStart hook.
7
+ * Returns a HookOutput with systemMessage containing the LATEST.md content.
8
+ */
9
+ export function restoreContext(options) {
10
+ const config = loadConfig(options.cwd);
11
+ const storagePath = getStoragePath(options.cwd, config);
12
+ const state = loadState(storagePath);
13
+ // Handle session state transitions
14
+ handleSessionTransition(storagePath, state, options, config.thresholds);
15
+ // Check if restoration is needed
16
+ if (!config.restoreOnSessionStart) {
17
+ return {};
18
+ }
19
+ // On resume, context is likely intact — skip restoration
20
+ if (options.source === 'resume') {
21
+ return {};
22
+ }
23
+ // Check for existing snapshots
24
+ const snapshotCount = getSnapshotCount(storagePath);
25
+ if (snapshotCount === 0) {
26
+ // First run — tell Claude that bookmark is active
27
+ return {
28
+ systemMessage: '[Bookmark: Active — no snapshots yet. Snapshots will be captured automatically ' +
29
+ 'before compaction, on 20-minute intervals, and at session end.]\n\n' +
30
+ 'Briefly let the user know that Bookmark is active and will start capturing context snapshots automatically.',
31
+ };
32
+ }
33
+ // Read LATEST.md
34
+ const latestMd = readLatestMd(storagePath);
35
+ if (!latestMd) {
36
+ return {};
37
+ }
38
+ if (options.format === 'json') {
39
+ const snapshot = loadLatestSnapshot(storagePath);
40
+ return {
41
+ systemMessage: JSON.stringify(snapshot, null, 2),
42
+ };
43
+ }
44
+ if (options.format === 'markdown') {
45
+ return {
46
+ systemMessage: latestMd,
47
+ };
48
+ }
49
+ // Default: system_message format
50
+ const systemMessage = buildRestorationMessage(latestMd, snapshotCount);
51
+ return { systemMessage };
52
+ }
53
+ function buildRestorationMessage(latestMd, snapshotCount) {
54
+ const lines = [];
55
+ lines.push('[Bookmark: Context recovered from previous session]');
56
+ lines.push('');
57
+ lines.push('Briefly notify the user that their prior session context has been recovered by Bookmark.');
58
+ lines.push('');
59
+ lines.push('## Prior Session Context (Bookmark)');
60
+ lines.push('');
61
+ lines.push(latestMd);
62
+ lines.push('');
63
+ if (snapshotCount > 1) {
64
+ lines.push(`> ${snapshotCount} snapshots available. Use \`/bookmark:list\` for history or \`/bookmark:restore\` for a specific snapshot.`);
65
+ }
66
+ return lines.join('\n');
67
+ }
68
+ function handleSessionTransition(storagePath, state, options, thresholds) {
69
+ const source = options.source ?? 'startup';
70
+ const sessionId = options.sessionId ?? `session_${Date.now()}`;
71
+ let updatedState;
72
+ switch (source) {
73
+ case 'startup':
74
+ case 'clear':
75
+ // New session — reset compaction count
76
+ updatedState = resetForNewSession(state, sessionId, thresholds);
77
+ break;
78
+ case 'compact':
79
+ // After compaction — increment compaction count
80
+ updatedState = incrementCompaction(state, thresholds);
81
+ updatedState.session_id = sessionId;
82
+ break;
83
+ case 'resume':
84
+ // Resuming — keep state, just update session ID if different
85
+ updatedState = { ...state, session_id: sessionId, last_event_time: Date.now() };
86
+ break;
87
+ default:
88
+ updatedState = state;
89
+ }
90
+ if (!existsSync(storagePath))
91
+ return;
92
+ saveState(storagePath, updatedState);
93
+ }
94
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/restore/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Check if bookmark hooks are already configured in a project.
3
+ */
4
+ export declare function isProjectConfigured(cwd: string): boolean;
5
+ /**
6
+ * Set up bookmark in a project directory.
7
+ * Creates storage dirs, configures hooks, injects gitignore and CLAUDE.md.
8
+ * Returns list of steps completed. Silent (no console output) — caller decides output.
9
+ */
10
+ export declare function setupProject(cwd: string): string[];
11
+ /**
12
+ * Auto-bootstrap: silently configure hooks if bookmark CLI runs in an unconfigured project.
13
+ * Called at the top of CLI commands. No-op if already configured.
14
+ */
15
+ export declare function ensureProjectBootstrapped(cwd: string): void;
16
+ //# sourceMappingURL=auto-setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-setup.d.ts","sourceRoot":"","sources":["../../src/setup/auto-setup.ts"],"names":[],"mappings":"AAqBA;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CASxD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,EAAE,CAgClD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAW3D"}
@@ -0,0 +1,192 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { configureHooks } from './configure-hooks.js';
4
+ const GREEN = '\x1b[32m';
5
+ const CYAN = '\x1b[36m';
6
+ const DIM = '\x1b[2m';
7
+ const RESET = '\x1b[0m';
8
+ /**
9
+ * Find the project root — npm sets INIT_CWD to the original working directory.
10
+ */
11
+ function findProjectRoot() {
12
+ if (process.env.INIT_CWD) {
13
+ return process.env.INIT_CWD;
14
+ }
15
+ return process.cwd();
16
+ }
17
+ // ─── Core Setup (reusable) ───
18
+ /**
19
+ * Check if bookmark hooks are already configured in a project.
20
+ */
21
+ export function isProjectConfigured(cwd) {
22
+ const settingsPath = join(cwd, '.claude', 'settings.json');
23
+ if (!existsSync(settingsPath))
24
+ return false;
25
+ try {
26
+ const content = readFileSync(settingsPath, 'utf-8');
27
+ return content.includes('@tyroneross/bookmark');
28
+ }
29
+ catch {
30
+ return false;
31
+ }
32
+ }
33
+ /**
34
+ * Set up bookmark in a project directory.
35
+ * Creates storage dirs, configures hooks, injects gitignore and CLAUDE.md.
36
+ * Returns list of steps completed. Silent (no console output) — caller decides output.
37
+ */
38
+ export function setupProject(cwd) {
39
+ const steps = [];
40
+ // 1. Ensure storage directories
41
+ const bookmarkPath = join(cwd, '.claude', 'bookmarks');
42
+ mkdirSync(join(bookmarkPath, 'snapshots'), { recursive: true });
43
+ mkdirSync(join(bookmarkPath, 'archive'), { recursive: true });
44
+ steps.push('Created .claude/bookmarks/ directories');
45
+ // 2. Configure hooks in settings.json
46
+ try {
47
+ configureHooks(cwd);
48
+ steps.push('Configured 4 hooks (PreCompact, SessionStart, UserPromptSubmit, Stop)');
49
+ }
50
+ catch { /* silently skip */ }
51
+ // 3. Add .claude/bookmarks/ to .gitignore
52
+ try {
53
+ const added = injectGitignore(cwd);
54
+ if (added) {
55
+ steps.push('Added .claude/bookmarks/ to .gitignore');
56
+ }
57
+ }
58
+ catch { /* silently skip */ }
59
+ // 4. Inject CLAUDE.md section
60
+ try {
61
+ const updated = injectClaudeMd(cwd);
62
+ if (updated) {
63
+ steps.push('Updated .claude/CLAUDE.md with bookmark docs');
64
+ }
65
+ }
66
+ catch { /* silently skip */ }
67
+ return steps;
68
+ }
69
+ /**
70
+ * Auto-bootstrap: silently configure hooks if bookmark CLI runs in an unconfigured project.
71
+ * Called at the top of CLI commands. No-op if already configured.
72
+ */
73
+ export function ensureProjectBootstrapped(cwd) {
74
+ if (isProjectConfigured(cwd))
75
+ return;
76
+ // Skip if no package.json (not a real project)
77
+ if (!existsSync(join(cwd, 'package.json')))
78
+ return;
79
+ try {
80
+ setupProject(cwd);
81
+ }
82
+ catch {
83
+ // Silent — don't break the command
84
+ }
85
+ }
86
+ // ─── Postinstall Entry Point ───
87
+ /**
88
+ * Auto-setup runs on npm postinstall.
89
+ * Sets up hooks, CLAUDE.md injection, and storage directories.
90
+ * Shows visible output so users know setup succeeded.
91
+ */
92
+ function autoSetup() {
93
+ // Skip in CI or when disabled
94
+ if (process.env.CI || process.env.BOOKMARK_SKIP_SETUP === 'true') {
95
+ return;
96
+ }
97
+ const projectRoot = findProjectRoot();
98
+ const isGlobal = process.env.npm_config_global === 'true';
99
+ const hasProject = projectRoot !== '/' && existsSync(join(projectRoot, 'package.json'));
100
+ // Global install with no project context
101
+ if (isGlobal && !hasProject) {
102
+ console.log(`\n ${GREEN}Bookmark${RESET} installed globally.`);
103
+ console.log(` Hooks will auto-configure when you use ${CYAN}/bookmark:activate${RESET} in a Claude Code session.\n`);
104
+ return;
105
+ }
106
+ // No project found
107
+ if (!hasProject) {
108
+ return;
109
+ }
110
+ // Skip if this is our own package (development mode)
111
+ try {
112
+ const pkg = JSON.parse(readFileSync(join(projectRoot, 'package.json'), 'utf-8'));
113
+ if (pkg.name === '@tyroneross/bookmark')
114
+ return;
115
+ }
116
+ catch { /* ignore */ }
117
+ // Global install FROM a project directory — configure that project
118
+ // Local install — configure the project
119
+ console.log(`\n ${GREEN}Bookmark${RESET} — Setting up context snapshots\n`);
120
+ try {
121
+ const steps = setupProject(projectRoot);
122
+ for (const step of steps) {
123
+ console.log(` ${GREEN}+${RESET} ${step}`);
124
+ }
125
+ console.log();
126
+ console.log(` ${GREEN}Ready.${RESET} Context snapshots are now active.`);
127
+ console.log();
128
+ console.log(` ${DIM}How it works:${RESET}`);
129
+ console.log(` ${DIM} - Snapshots captured automatically before compaction and on intervals${RESET}`);
130
+ console.log(` ${DIM} - Context restored when you start a new session${RESET}`);
131
+ console.log(` ${DIM} - Use${RESET} ${CYAN}/bookmark:snapshot${RESET} ${DIM}for manual snapshots${RESET}`);
132
+ console.log();
133
+ }
134
+ catch (error) {
135
+ // Don't fail npm install
136
+ console.warn(` Setup skipped: ${error.message}\n`);
137
+ }
138
+ }
139
+ // ─── Inject Helpers ───
140
+ function injectGitignore(cwd) {
141
+ const gitignorePath = join(cwd, '.gitignore');
142
+ const entry = '.claude/bookmarks/';
143
+ let content = '';
144
+ if (existsSync(gitignorePath)) {
145
+ content = readFileSync(gitignorePath, 'utf-8');
146
+ if (content.includes(entry))
147
+ return false;
148
+ if (!content.endsWith('\n'))
149
+ content += '\n';
150
+ }
151
+ content += `\n# Bookmark snapshot data\n${entry}\n`;
152
+ writeFileSync(gitignorePath, content, 'utf-8');
153
+ return true;
154
+ }
155
+ function injectClaudeMd(cwd) {
156
+ const claudeMdPath = join(cwd, '.claude', 'CLAUDE.md');
157
+ const marker = '## Bookmark — Context Snapshots';
158
+ let content = '';
159
+ if (existsSync(claudeMdPath)) {
160
+ content = readFileSync(claudeMdPath, 'utf-8');
161
+ // Don't duplicate
162
+ if (content.includes(marker))
163
+ return false;
164
+ content += '\n\n';
165
+ }
166
+ else {
167
+ // Ensure .claude dir exists
168
+ mkdirSync(join(cwd, '.claude'), { recursive: true });
169
+ }
170
+ content += `${marker}
171
+
172
+ This project uses @tyroneross/bookmark for context snapshots.
173
+
174
+ **Automatic behavior:**
175
+ - Snapshots captured before compaction and on session end
176
+ - Context restored automatically on session start
177
+ - Adaptive thresholds increase snapshot frequency with compaction count
178
+ - Time-based snapshots every 20 minutes (configurable)
179
+
180
+ **Commands:**
181
+ - \`/bookmark:snapshot\` — Manual snapshot
182
+ - \`/bookmark:restore\` — Restore from a snapshot
183
+ - \`/bookmark:status\` — Show snapshot stats
184
+ - \`/bookmark:list\` — List all snapshots
185
+
186
+ The system operates with zero context window tax — all processing runs externally.
187
+ `;
188
+ writeFileSync(claudeMdPath, content, 'utf-8');
189
+ return true;
190
+ }
191
+ autoSetup();
192
+ //# sourceMappingURL=auto-setup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-setup.js","sourceRoot":"","sources":["../../src/setup/auto-setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Configure hooks in the project's .claude/settings.json.
3
+ * Adds bookmark hooks without duplicating or overwriting existing hooks.
4
+ */
5
+ export declare function configureHooks(cwd: string): void;
6
+ /**
7
+ * Remove bookmark hooks from settings.json.
8
+ */
9
+ export declare function removeHooks(cwd: string): void;
10
+ //# sourceMappingURL=configure-hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure-hooks.d.ts","sourceRoot":"","sources":["../../src/setup/configure-hooks.ts"],"names":[],"mappings":"AA2DA;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAqChD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAqB7C"}
@@ -0,0 +1,97 @@
1
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ const BOOKMARK_HOOKS = {
4
+ PreCompact: {
5
+ matcher: '',
6
+ hooks: [{
7
+ type: 'command',
8
+ command: 'npx @tyroneross/bookmark snapshot --trigger pre_compact 2>/dev/null || true',
9
+ timeout: 30000,
10
+ async: true,
11
+ }],
12
+ },
13
+ SessionStart: {
14
+ matcher: '',
15
+ hooks: [{
16
+ type: 'command',
17
+ command: 'npx @tyroneross/bookmark restore 2>/dev/null || echo \'{}\'',
18
+ timeout: 5000,
19
+ }],
20
+ },
21
+ UserPromptSubmit: {
22
+ matcher: '',
23
+ hooks: [{
24
+ type: 'command',
25
+ command: 'npx @tyroneross/bookmark check 2>/dev/null || true',
26
+ timeout: 3000,
27
+ async: true,
28
+ }],
29
+ },
30
+ Stop: {
31
+ matcher: '',
32
+ hooks: [{
33
+ type: 'command',
34
+ command: 'npx @tyroneross/bookmark snapshot --trigger session_end 2>/dev/null || true',
35
+ timeout: 15000,
36
+ }],
37
+ },
38
+ };
39
+ const BOOKMARK_MARKER = '@tyroneross/bookmark';
40
+ /**
41
+ * Configure hooks in the project's .claude/settings.json.
42
+ * Adds bookmark hooks without duplicating or overwriting existing hooks.
43
+ */
44
+ export function configureHooks(cwd) {
45
+ const settingsDir = join(cwd, '.claude');
46
+ const settingsPath = join(settingsDir, 'settings.json');
47
+ if (!existsSync(settingsDir)) {
48
+ mkdirSync(settingsDir, { recursive: true });
49
+ }
50
+ let settings = {};
51
+ if (existsSync(settingsPath)) {
52
+ try {
53
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
54
+ }
55
+ catch {
56
+ settings = {};
57
+ }
58
+ }
59
+ if (!settings.hooks) {
60
+ settings.hooks = {};
61
+ }
62
+ for (const [event, hookConfig] of Object.entries(BOOKMARK_HOOKS)) {
63
+ if (!settings.hooks[event]) {
64
+ settings.hooks[event] = [];
65
+ }
66
+ // Check if bookmark hook already exists
67
+ const exists = settings.hooks[event].some(h => h.hooks.some(hh => hh.command?.includes(BOOKMARK_MARKER)));
68
+ if (!exists) {
69
+ settings.hooks[event].push(hookConfig);
70
+ }
71
+ }
72
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
73
+ }
74
+ /**
75
+ * Remove bookmark hooks from settings.json.
76
+ */
77
+ export function removeHooks(cwd) {
78
+ const settingsPath = join(cwd, '.claude', 'settings.json');
79
+ if (!existsSync(settingsPath))
80
+ return;
81
+ try {
82
+ const settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
83
+ if (!settings.hooks)
84
+ return;
85
+ for (const event of Object.keys(settings.hooks)) {
86
+ settings.hooks[event] = settings.hooks[event].filter(h => !h.hooks.some(hh => hh.command?.includes(BOOKMARK_MARKER)));
87
+ if (settings.hooks[event].length === 0) {
88
+ delete settings.hooks[event];
89
+ }
90
+ }
91
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf-8');
92
+ }
93
+ catch {
94
+ // Silent
95
+ }
96
+ }
97
+ //# sourceMappingURL=configure-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"configure-hooks.js","sourceRoot":"","sources":["../../src/setup/configure-hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { Snapshot, SnapshotTrigger } from '../types.js';
2
+ export interface CaptureOptions {
3
+ trigger: SnapshotTrigger;
4
+ transcriptPath: string;
5
+ cwd: string;
6
+ sessionId?: string;
7
+ smart?: boolean;
8
+ }
9
+ /**
10
+ * Full snapshot capture pipeline:
11
+ * 1. Parse transcript
12
+ * 2. Estimate token usage
13
+ * 3. Extract decisions/status/files/errors
14
+ * 4. Optionally enhance with LLM (--smart)
15
+ * 5. Build snapshot object
16
+ * 6. Store snapshot JSON
17
+ * 7. Generate and write LATEST.md
18
+ * 8. Update state
19
+ */
20
+ export declare function captureSnapshot(options: CaptureOptions): Promise<Snapshot>;
21
+ //# sourceMappingURL=capture.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.d.ts","sourceRoot":"","sources":["../../src/snapshot/capture.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE7D,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,eAAe,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyDhF"}
@@ -0,0 +1,134 @@
1
+ import { parseTranscript } from '../transcript/parser.js';
2
+ import { estimateFromTranscript } from '../transcript/estimator.js';
3
+ import { extractFromEntries } from '../transcript/extractor.js';
4
+ import { storeSnapshot, writeLatestMd, loadLatestSnapshot } from './storage.js';
5
+ import { compressToMarkdown } from './compress.js';
6
+ import { loadState, saveState, updateSnapshotTime } from '../threshold/state.js';
7
+ import { loadConfig, getStoragePath } from '../config.js';
8
+ /**
9
+ * Full snapshot capture pipeline:
10
+ * 1. Parse transcript
11
+ * 2. Estimate token usage
12
+ * 3. Extract decisions/status/files/errors
13
+ * 4. Optionally enhance with LLM (--smart)
14
+ * 5. Build snapshot object
15
+ * 6. Store snapshot JSON
16
+ * 7. Generate and write LATEST.md
17
+ * 8. Update state
18
+ */
19
+ export async function captureSnapshot(options) {
20
+ const config = loadConfig(options.cwd);
21
+ const storagePath = getStoragePath(options.cwd, config);
22
+ const state = loadState(storagePath);
23
+ // 1. Parse transcript
24
+ const { entries } = parseTranscript(options.transcriptPath);
25
+ // 2. Estimate token usage
26
+ const estimate = estimateFromTranscript(options.transcriptPath, {
27
+ contextLimit: config.contextLimitTokens,
28
+ charsPerToken: config.charsPerToken,
29
+ });
30
+ // 3. Extract structured content
31
+ let extraction = extractFromEntries(entries);
32
+ // 4. Optional LLM enhancement
33
+ if (options.smart ?? config.smartDefault) {
34
+ extraction = await enhanceWithLLM(extraction, entries);
35
+ }
36
+ // 5. Build snapshot
37
+ const snapshotId = generateSnapshotId();
38
+ const priorSnapshot = loadLatestSnapshot(storagePath);
39
+ const snapshot = {
40
+ snapshot_id: snapshotId,
41
+ timestamp: Date.now(),
42
+ session_id: options.sessionId ?? state.session_id ?? 'unknown',
43
+ project_path: options.cwd,
44
+ trigger: options.trigger,
45
+ compaction_cycle: state.compaction_count,
46
+ context_remaining_pct: estimate.remaining_pct,
47
+ token_estimate: estimate.total_tokens,
48
+ current_status: extraction.current_status,
49
+ decisions: extraction.decisions,
50
+ open_items: extraction.open_items,
51
+ unknowns: extraction.unknowns,
52
+ files_changed: extraction.files_changed,
53
+ errors_encountered: extraction.errors_encountered,
54
+ tools_summary: extraction.tools_summary,
55
+ prior_snapshot_id: priorSnapshot?.snapshot_id,
56
+ };
57
+ // 6. Store snapshot
58
+ storeSnapshot(storagePath, snapshot);
59
+ // 7. Generate LATEST.md
60
+ const markdown = compressToMarkdown(snapshot);
61
+ writeLatestMd(storagePath, markdown);
62
+ // 8. Update state
63
+ const updatedState = updateSnapshotTime(state);
64
+ saveState(storagePath, updatedState);
65
+ return snapshot;
66
+ }
67
+ function generateSnapshotId() {
68
+ const now = new Date();
69
+ const pad = (n, len = 2) => String(n).padStart(len, '0');
70
+ const date = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}`;
71
+ const time = `${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
72
+ return `SNAP_${date}_${time}`;
73
+ }
74
+ /**
75
+ * Enhance extraction with Claude Haiku for higher-quality summaries.
76
+ * Falls back to original extraction if SDK not available or API key missing.
77
+ */
78
+ async function enhanceWithLLM(extraction, entries) {
79
+ const apiKey = process.env.ANTHROPIC_API_KEY;
80
+ if (!apiKey)
81
+ return extraction;
82
+ try {
83
+ // Dynamic import — @anthropic-ai/sdk is an optional dependency
84
+ const { default: Anthropic } = await import('@anthropic-ai/sdk');
85
+ const client = new Anthropic({ apiKey });
86
+ // Build a compressed transcript excerpt (last ~2000 tokens worth)
87
+ const recentEntries = entries.slice(-50); // Last 50 entries
88
+ const transcript = recentEntries
89
+ .filter(e => e.type === 'assistant' || e.type === 'user')
90
+ .map(e => `[${e.type}]: ${typeof e.content === 'string' ? e.content.slice(0, 200) : ''}`)
91
+ .join('\n')
92
+ .slice(-4000);
93
+ const response = await client.messages.create({
94
+ model: 'claude-haiku-4-5-20251001',
95
+ max_tokens: 1000,
96
+ messages: [{
97
+ role: 'user',
98
+ content: `Analyze this coding session transcript and extract:
99
+ 1. Key decisions made (with brief rationale)
100
+ 2. Current status (1-2 sentences)
101
+ 3. Open items/TODOs (with priority: high/medium/low)
102
+ 4. Unknowns or blockers
103
+
104
+ Transcript excerpt:
105
+ ${transcript}
106
+
107
+ Respond in JSON format:
108
+ {
109
+ "current_status": "...",
110
+ "decisions": [{"description": "...", "rationale": "..."}],
111
+ "open_items": [{"description": "...", "priority": "high|medium|low"}],
112
+ "unknowns": ["..."]
113
+ }`,
114
+ }],
115
+ });
116
+ const text = response.content[0]?.type === 'text' ? response.content[0].text : '';
117
+ const jsonMatch = text.match(/\{[\s\S]*\}/);
118
+ if (jsonMatch) {
119
+ const enhanced = JSON.parse(jsonMatch[0]);
120
+ return {
121
+ ...extraction,
122
+ current_status: enhanced.current_status ?? extraction.current_status,
123
+ decisions: enhanced.decisions?.length ? enhanced.decisions : extraction.decisions,
124
+ open_items: enhanced.open_items?.length ? enhanced.open_items : extraction.open_items,
125
+ unknowns: enhanced.unknowns?.length ? enhanced.unknowns : extraction.unknowns,
126
+ };
127
+ }
128
+ }
129
+ catch {
130
+ // Fall back silently to pattern-based extraction
131
+ }
132
+ return extraction;
133
+ }
134
+ //# sourceMappingURL=capture.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capture.js","sourceRoot":"","sources":["../../src/snapshot/capture.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Snapshot } from '../types.js';
2
+ /**
3
+ * Compress a full snapshot into a concise markdown summary.
4
+ * Target: <150 lines, <1000 tokens.
5
+ * This becomes LATEST.md — the hot context tier.
6
+ */
7
+ export declare function compressToMarkdown(snapshot: Snapshot): string;
8
+ //# sourceMappingURL=compress.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compress.d.ts","sourceRoot":"","sources":["../../src/snapshot/compress.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CA2F7D"}