@tyroneross/bookmark 0.1.0 → 0.3.2

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 (109) hide show
  1. package/.claude-plugin/marketplace.json +29 -12
  2. package/.claude-plugin/plugin.json +12 -8
  3. package/.codex-plugin/plugin.json +31 -0
  4. package/.mcp.json +8 -0
  5. package/CLAUDE.md +83 -49
  6. package/LICENSE +202 -21
  7. package/README.md +150 -57
  8. package/agents/snapshot-analyst.md +1 -1
  9. package/commands/bookmark.md +29 -0
  10. package/commands/feedback.md +37 -0
  11. package/commands/restore.md +14 -4
  12. package/commands/snapshot.md +19 -7
  13. package/commands/status.md +1 -1
  14. package/dist/cli/index.js +627 -87
  15. package/dist/cli/index.js.map +1 -1
  16. package/dist/cli/snapshot-resolution.d.ts +15 -0
  17. package/dist/cli/snapshot-resolution.d.ts.map +1 -0
  18. package/dist/cli/snapshot-resolution.js +21 -0
  19. package/dist/cli/snapshot-resolution.js.map +1 -0
  20. package/dist/config.d.ts +19 -0
  21. package/dist/config.d.ts.map +1 -1
  22. package/dist/config.js +149 -23
  23. package/dist/config.js.map +1 -1
  24. package/dist/context/freshness.d.ts +3 -0
  25. package/dist/context/freshness.d.ts.map +1 -0
  26. package/dist/context/freshness.js +29 -0
  27. package/dist/context/freshness.js.map +1 -0
  28. package/dist/context/handoff-prompt.d.ts +12 -0
  29. package/dist/context/handoff-prompt.d.ts.map +1 -0
  30. package/dist/context/handoff-prompt.js +29 -0
  31. package/dist/context/handoff-prompt.js.map +1 -0
  32. package/dist/index.d.ts +7 -4
  33. package/dist/index.d.ts.map +1 -1
  34. package/dist/index.js +5 -3
  35. package/dist/index.js.map +1 -1
  36. package/dist/mcp/server.d.ts +9 -0
  37. package/dist/mcp/server.d.ts.map +1 -0
  38. package/dist/mcp/server.js +85 -0
  39. package/dist/mcp/server.js.map +1 -0
  40. package/dist/mcp/tools.d.ts +97 -0
  41. package/dist/mcp/tools.d.ts.map +1 -0
  42. package/dist/mcp/tools.js +304 -0
  43. package/dist/mcp/tools.js.map +1 -0
  44. package/dist/registry.d.ts +40 -0
  45. package/dist/registry.d.ts.map +1 -0
  46. package/dist/registry.js +134 -0
  47. package/dist/registry.js.map +1 -0
  48. package/dist/restore/index.d.ts +13 -1
  49. package/dist/restore/index.d.ts.map +1 -1
  50. package/dist/restore/index.js +163 -43
  51. package/dist/restore/index.js.map +1 -1
  52. package/dist/setup/auto-setup.d.ts +8 -2
  53. package/dist/setup/auto-setup.d.ts.map +1 -1
  54. package/dist/setup/auto-setup.js +287 -67
  55. package/dist/setup/auto-setup.js.map +1 -1
  56. package/dist/setup/configure-hooks.d.ts +14 -3
  57. package/dist/setup/configure-hooks.d.ts.map +1 -1
  58. package/dist/setup/configure-hooks.js +156 -24
  59. package/dist/setup/configure-hooks.js.map +1 -1
  60. package/dist/snapshot/capture.d.ts +13 -9
  61. package/dist/snapshot/capture.d.ts.map +1 -1
  62. package/dist/snapshot/capture.js +42 -105
  63. package/dist/snapshot/capture.js.map +1 -1
  64. package/dist/snapshot/compress.d.ts +3 -3
  65. package/dist/snapshot/compress.d.ts.map +1 -1
  66. package/dist/snapshot/compress.js +35 -67
  67. package/dist/snapshot/compress.js.map +1 -1
  68. package/dist/snapshot/storage.d.ts +7 -0
  69. package/dist/snapshot/storage.d.ts.map +1 -1
  70. package/dist/snapshot/storage.js +31 -6
  71. package/dist/snapshot/storage.js.map +1 -1
  72. package/dist/threshold/state.d.ts +5 -0
  73. package/dist/threshold/state.d.ts.map +1 -1
  74. package/dist/threshold/state.js +28 -2
  75. package/dist/threshold/state.js.map +1 -1
  76. package/dist/threshold/token-usage.d.ts +29 -0
  77. package/dist/threshold/token-usage.d.ts.map +1 -0
  78. package/dist/threshold/token-usage.js +128 -0
  79. package/dist/threshold/token-usage.js.map +1 -0
  80. package/dist/trails/identity.d.ts +76 -0
  81. package/dist/trails/identity.d.ts.map +1 -0
  82. package/dist/trails/identity.js +117 -0
  83. package/dist/trails/identity.js.map +1 -0
  84. package/dist/trails/reader.d.ts +7 -0
  85. package/dist/trails/reader.d.ts.map +1 -0
  86. package/dist/trails/reader.js +22 -0
  87. package/dist/trails/reader.js.map +1 -0
  88. package/dist/trails/writer.d.ts +9 -0
  89. package/dist/trails/writer.d.ts.map +1 -0
  90. package/dist/trails/writer.js +110 -0
  91. package/dist/trails/writer.js.map +1 -0
  92. package/dist/transcript/extractor.d.ts +17 -4
  93. package/dist/transcript/extractor.d.ts.map +1 -1
  94. package/dist/transcript/extractor.js +141 -174
  95. package/dist/transcript/extractor.js.map +1 -1
  96. package/dist/transcript/parser.d.ts.map +1 -1
  97. package/dist/transcript/parser.js +69 -7
  98. package/dist/transcript/parser.js.map +1 -1
  99. package/dist/types.d.ts +37 -38
  100. package/dist/types.d.ts.map +1 -1
  101. package/hooks/hooks.json +12 -14
  102. package/package.json +13 -10
  103. package/scripts/install-plugin.sh +48 -0
  104. package/skills/context-continuity/SKILL.md +43 -24
  105. package/commands/activate.md +0 -20
  106. package/dist/transcript/estimator.d.ts +0 -20
  107. package/dist/transcript/estimator.d.ts.map +0 -1
  108. package/dist/transcript/estimator.js +0 -95
  109. package/dist/transcript/estimator.js.map +0 -1
@@ -0,0 +1,117 @@
1
+ /**
2
+ * BOOKMARK_IDENTITY block parser + validator.
3
+ *
4
+ * The identity block is an HTML comment at the top of bookmark.context.md
5
+ * that declares which project/repo/branch the summary belongs to. It makes
6
+ * cross-session restoration unambiguous — you can always verify the
7
+ * bookmark you're restoring from matches the CWD you're restoring into.
8
+ *
9
+ * Example:
10
+ * <!-- BOOKMARK_IDENTITY
11
+ * scope: repo
12
+ * project: travel-planner
13
+ * repo_path: /Users/me/Desktop/git-folder/Travel Planner
14
+ * branch: feature/summer-camps
15
+ * head: 4988383
16
+ * written: 2026-04-11
17
+ * -->
18
+ *
19
+ * Two scopes are supported:
20
+ * - `repo` — bookmark belongs to a specific git repo (or directory)
21
+ * - `home` — bookmark is a POINTER, no active context itself; delegates
22
+ * via `points_to_canonical` to a repo-scoped bookmark
23
+ */
24
+ import { existsSync, readFileSync, statSync } from 'node:fs';
25
+ import { resolve } from 'node:path';
26
+ const IDENTITY_BLOCK = /<!--\s*BOOKMARK_IDENTITY\s*\n([\s\S]*?)\n\s*-->/;
27
+ /**
28
+ * Parse a bookmark.context.md file's content and extract the identity block.
29
+ * Returns `identity: null` if no block is present — callers should treat that
30
+ * as a legacy/untagged file and fall back to current behavior.
31
+ */
32
+ export function parseIdentity(content) {
33
+ const match = content.match(IDENTITY_BLOCK);
34
+ if (!match) {
35
+ return { identity: null, bodyWithoutIdentity: content };
36
+ }
37
+ const raw = {};
38
+ for (const line of match[1].split('\n')) {
39
+ const colon = line.indexOf(':');
40
+ if (colon < 0)
41
+ continue;
42
+ const key = line.slice(0, colon).trim();
43
+ const value = line.slice(colon + 1).trim();
44
+ if (!key)
45
+ continue;
46
+ raw[key] = value;
47
+ }
48
+ const scopeRaw = (raw.scope ?? 'repo').toLowerCase();
49
+ const scope = scopeRaw === 'home' ? 'home' : 'repo';
50
+ const identity = {
51
+ scope,
52
+ project: raw.project,
53
+ repo_path: raw.repo_path,
54
+ repo_name: raw.repo_name,
55
+ branch: raw.branch,
56
+ head: raw.head,
57
+ base: raw.base,
58
+ written: raw.written,
59
+ written_by: raw.written_by,
60
+ points_to_project: raw.points_to_project,
61
+ points_to_repo: raw.points_to_repo,
62
+ points_to_canonical: raw.points_to_canonical,
63
+ raw,
64
+ };
65
+ // Body with block stripped — preserve surrounding content
66
+ const bodyWithoutIdentity = content.slice(0, match.index) + content.slice(match.index + match[0].length);
67
+ return { identity, bodyWithoutIdentity: bodyWithoutIdentity.replace(/^\s*\n+/, '') };
68
+ }
69
+ /**
70
+ * Validate a repo-scoped identity against the storage path that was loaded.
71
+ *
72
+ * Returns a warning string if the identity's `repo_path` disagrees with the
73
+ * working directory, suggesting the bookmark file was moved or the CWD is
74
+ * not the repo it describes. Returns null when they match or when we can't
75
+ * determine (missing field).
76
+ */
77
+ export function validateRepoIdentity(identity, cwd) {
78
+ if (identity.scope !== 'repo')
79
+ return null;
80
+ if (!identity.repo_path)
81
+ return null;
82
+ const declared = resolve(identity.repo_path);
83
+ const actual = resolve(cwd);
84
+ if (declared === actual)
85
+ return null;
86
+ // Allow the case where CWD is a subdirectory of the declared repo
87
+ if (actual.startsWith(declared + '/'))
88
+ return null;
89
+ return (`[Bookmark identity mismatch] This bookmark.context.md declares ` +
90
+ `repo_path=${declared} but was loaded from cwd=${actual}. ` +
91
+ `Verify you are in the correct project before acting on the context below.`);
92
+ }
93
+ export function followPointer(identity) {
94
+ if (identity.scope !== 'home')
95
+ return null;
96
+ if (!identity.points_to_canonical)
97
+ return null;
98
+ const target = identity.points_to_canonical;
99
+ if (!existsSync(target))
100
+ return null;
101
+ try {
102
+ const content = readFileSync(target, 'utf-8');
103
+ const mtime = statSync(target).mtimeMs;
104
+ const staleness_hours = Math.round((Date.now() - mtime) / (1000 * 60 * 60));
105
+ const { identity: targetIdentity } = parseIdentity(content);
106
+ return {
107
+ canonical_path: target,
108
+ content,
109
+ identity: targetIdentity,
110
+ staleness_hours,
111
+ };
112
+ }
113
+ catch {
114
+ return null;
115
+ }
116
+ }
117
+ //# sourceMappingURL=identity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"identity.js","sourceRoot":"","sources":["../../src/trails/identity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Read the bookmark.context.md trailhead file.
3
+ * This is the primary restoration source — compact (<400 tokens)
4
+ * with routing pointers to deeper trail files.
5
+ */
6
+ export declare function readContextMd(storagePath: string): string | null;
7
+ //# sourceMappingURL=reader.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.d.ts","sourceRoot":"","sources":["../../src/trails/reader.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUhE"}
@@ -0,0 +1,22 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Read the bookmark.context.md trailhead file.
5
+ * This is the primary restoration source — compact (<400 tokens)
6
+ * with routing pointers to deeper trail files.
7
+ */
8
+ export function readContextMd(storagePath) {
9
+ const contextPath = join(storagePath, 'bookmark.context.md');
10
+ if (!existsSync(contextPath))
11
+ return null;
12
+ try {
13
+ const content = readFileSync(contextPath, 'utf-8');
14
+ if (!content.trim())
15
+ return null;
16
+ return content;
17
+ }
18
+ catch {
19
+ return null;
20
+ }
21
+ }
22
+ //# sourceMappingURL=reader.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reader.js","sourceRoot":"","sources":["../../src/trails/reader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC;;;;GAIG;AACH,MAAM,UAAU,aAAa,CAAC,WAAmB;IAC/C,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,EAAE,qBAAqB,CAAC,CAAC;IAC7D,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC;QAAE,OAAO,IAAI,CAAC;IAC1C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;QACnD,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE;YAAE,OAAO,IAAI,CAAC;QACjC,OAAO,OAAO,CAAC;IACjB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { Snapshot } from '../types.js';
2
+ /**
3
+ * Trail-routed memory writer.
4
+ *
5
+ * Writes file tracking data to trails/files.md.
6
+ * bookmark.context.md is Claude's domain — written via Stop/PreCompact prompt hooks.
7
+ */
8
+ export declare function writeTrails(storagePath: string, snapshot: Snapshot): void;
9
+ //# sourceMappingURL=writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/trails/writer.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAgB,MAAM,aAAa,CAAC;AAE1D;;;;;GAKG;AAIH,wBAAgB,WAAW,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAGzE"}
@@ -0,0 +1,110 @@
1
+ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ /**
4
+ * Trail-routed memory writer.
5
+ *
6
+ * Writes file tracking data to trails/files.md.
7
+ * bookmark.context.md is Claude's domain — written via Stop/PreCompact prompt hooks.
8
+ */
9
+ // ─── Public API ───
10
+ export function writeTrails(storagePath, snapshot) {
11
+ const trailsDir = ensureTrailsDir(storagePath);
12
+ writeFileTrail(trailsDir, snapshot);
13
+ }
14
+ // ─── File Trail ───
15
+ function writeFileTrail(trailsDir, snapshot) {
16
+ const trailPath = join(trailsDir, 'files.md');
17
+ // Load existing file entries and merge with new
18
+ const fileMap = loadFileEntries(trailPath);
19
+ for (const f of snapshot.files_changed) {
20
+ const shortPath = f.path.startsWith(snapshot.project_path)
21
+ ? f.path.slice(snapshot.project_path.length).replace(/^\//, '')
22
+ : f.path;
23
+ const existing = fileMap.get(shortPath);
24
+ if (existing) {
25
+ const ops = new Set([...existing.operations, ...f.operations]);
26
+ existing.operations = [...ops];
27
+ // Use max, not sum — same transcript produces same totals across snapshots
28
+ existing.lines_changed = Math.max(existing.lines_changed ?? 0, f.lines_changed ?? 0);
29
+ // Merge summaries: combine unique structural hints
30
+ if (f.summary) {
31
+ if (existing.summary) {
32
+ const existingHints = new Set(existing.summary.split(', '));
33
+ const newHints = f.summary.split(', ');
34
+ for (const h of newHints)
35
+ existingHints.add(h);
36
+ existing.summary = [...existingHints].slice(0, 6).join(', ');
37
+ }
38
+ else {
39
+ existing.summary = f.summary;
40
+ }
41
+ }
42
+ }
43
+ else {
44
+ fileMap.set(shortPath, {
45
+ path: shortPath,
46
+ operations: [...f.operations],
47
+ lines_changed: f.lines_changed,
48
+ summary: f.summary,
49
+ });
50
+ }
51
+ }
52
+ // Sort by lines changed descending
53
+ const files = [...fileMap.values()].sort((a, b) => (b.lines_changed ?? 0) - (a.lines_changed ?? 0));
54
+ const totalLines = files.reduce((sum, f) => sum + (f.lines_changed ?? 0), 0);
55
+ const lines = [];
56
+ lines.push('# File Trail');
57
+ lines.push(`> ${files.length} files, ~${totalLines} lines changed total`);
58
+ lines.push('');
59
+ for (const f of files) {
60
+ const lc = f.lines_changed ? ` ~${f.lines_changed}L` : '';
61
+ let line = `- \`${f.path}\` (${f.operations.join('/')}${lc})`;
62
+ if (f.summary) {
63
+ line += ` — ${f.summary}`;
64
+ }
65
+ lines.push(line);
66
+ }
67
+ writeFileSync(trailPath, lines.join('\n'), 'utf-8');
68
+ // Return top files with summaries for bookmark.context.md
69
+ const topFiles = files
70
+ .filter(f => f.summary)
71
+ .slice(0, 5)
72
+ .map(f => ({ path: f.path, summary: f.summary }));
73
+ return { count: files.length, totalLines, topFiles };
74
+ }
75
+ function loadFileEntries(trailPath) {
76
+ if (!existsSync(trailPath))
77
+ return new Map();
78
+ try {
79
+ const content = readFileSync(trailPath, 'utf-8');
80
+ const fileMap = new Map();
81
+ for (const line of content.split('\n')) {
82
+ // Parse: - `src/foo.ts` (edit/write ~123L) — +fn registerPlugin, +fn ensurePluginSymlink
83
+ const match = line.match(/^- `(.+?)` \(([^)]+)\)/);
84
+ if (match) {
85
+ const path = match[1];
86
+ const opsStr = match[2];
87
+ const ops = opsStr.replace(/\s*~\d+L/, '').split('/');
88
+ const lcMatch = opsStr.match(/~(\d+)L/);
89
+ const linesChanged = lcMatch ? parseInt(lcMatch[1], 10) : undefined;
90
+ // Extract summary after " — " if present
91
+ const summaryMatch = line.match(/\) — (.+)$/);
92
+ const summary = summaryMatch ? summaryMatch[1] : undefined;
93
+ fileMap.set(path, { path, operations: ops, lines_changed: linesChanged, summary });
94
+ }
95
+ }
96
+ return fileMap;
97
+ }
98
+ catch {
99
+ return new Map();
100
+ }
101
+ }
102
+ // ─── Helpers ───
103
+ function ensureTrailsDir(storagePath) {
104
+ const trailsDir = join(storagePath, 'trails');
105
+ if (!existsSync(trailsDir)) {
106
+ mkdirSync(trailsDir, { recursive: true });
107
+ }
108
+ return trailsDir;
109
+ }
110
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/trails/writer.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,20 @@
1
- import type { TranscriptEntry, ExtractionResult } from '../types.js';
1
+ import type { TranscriptEntry, FileActivity } from '../types.js';
2
+ export interface ExtractOptions {
3
+ projectPath?: string;
4
+ }
5
+ export interface FilesAndToolsResult {
6
+ files_changed: FileActivity[];
7
+ tools_summary: Record<string, number>;
8
+ }
2
9
  /**
3
- * Extract structured context from transcript entries using pattern matching.
4
- * Zero LLM calls pure heuristic extraction.
10
+ * Extract file changes and tool usage from transcript entries.
11
+ * These are the two extraction functions that actually produce accurate data.
5
12
  */
6
- export declare function extractFromEntries(entries: TranscriptEntry[]): ExtractionResult;
13
+ export declare function extractFilesAndTools(entries: TranscriptEntry[], options?: ExtractOptions): FilesAndToolsResult;
14
+ export interface CommitGap {
15
+ edits_since_commit: number;
16
+ files_since_commit: number;
17
+ last_commit_index: number;
18
+ }
19
+ export declare function extractCommitGap(entries: TranscriptEntry[]): CommitGap;
7
20
  //# sourceMappingURL=extractor.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/transcript/extractor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAA+D,MAAM,aAAa,CAAC;AA+BlI;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,gBAAgB,CAkB/E"}
1
+ {"version":3,"file":"extractor.d.ts","sourceRoot":"","sources":["../../src/transcript/extractor.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAiB,MAAM,aAAa,CAAC;AAyChF,MAAM,WAAW,cAAc;IAC7B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,YAAY,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACvC;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,mBAAmB,CAM9G;AAyID,MAAM,WAAW,SAAS;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,kBAAkB,EAAE,MAAM,CAAC;IAC3B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,eAAe,EAAE,GAAG,SAAS,CA+BtE"}