@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.
- package/.claude-plugin/marketplace.json +29 -12
- package/.claude-plugin/plugin.json +12 -8
- package/.codex-plugin/plugin.json +31 -0
- package/.mcp.json +8 -0
- package/CLAUDE.md +83 -49
- package/LICENSE +202 -21
- package/README.md +150 -57
- package/agents/snapshot-analyst.md +1 -1
- package/commands/bookmark.md +29 -0
- package/commands/feedback.md +37 -0
- package/commands/restore.md +14 -4
- package/commands/snapshot.md +19 -7
- package/commands/status.md +1 -1
- package/dist/cli/index.js +627 -87
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/snapshot-resolution.d.ts +15 -0
- package/dist/cli/snapshot-resolution.d.ts.map +1 -0
- package/dist/cli/snapshot-resolution.js +21 -0
- package/dist/cli/snapshot-resolution.js.map +1 -0
- package/dist/config.d.ts +19 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +149 -23
- package/dist/config.js.map +1 -1
- package/dist/context/freshness.d.ts +3 -0
- package/dist/context/freshness.d.ts.map +1 -0
- package/dist/context/freshness.js +29 -0
- package/dist/context/freshness.js.map +1 -0
- package/dist/context/handoff-prompt.d.ts +12 -0
- package/dist/context/handoff-prompt.d.ts.map +1 -0
- package/dist/context/handoff-prompt.js +29 -0
- package/dist/context/handoff-prompt.js.map +1 -0
- package/dist/index.d.ts +7 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +85 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/tools.d.ts +97 -0
- package/dist/mcp/tools.d.ts.map +1 -0
- package/dist/mcp/tools.js +304 -0
- package/dist/mcp/tools.js.map +1 -0
- package/dist/registry.d.ts +40 -0
- package/dist/registry.d.ts.map +1 -0
- package/dist/registry.js +134 -0
- package/dist/registry.js.map +1 -0
- package/dist/restore/index.d.ts +13 -1
- package/dist/restore/index.d.ts.map +1 -1
- package/dist/restore/index.js +163 -43
- package/dist/restore/index.js.map +1 -1
- package/dist/setup/auto-setup.d.ts +8 -2
- package/dist/setup/auto-setup.d.ts.map +1 -1
- package/dist/setup/auto-setup.js +287 -67
- package/dist/setup/auto-setup.js.map +1 -1
- package/dist/setup/configure-hooks.d.ts +14 -3
- package/dist/setup/configure-hooks.d.ts.map +1 -1
- package/dist/setup/configure-hooks.js +156 -24
- package/dist/setup/configure-hooks.js.map +1 -1
- package/dist/snapshot/capture.d.ts +13 -9
- package/dist/snapshot/capture.d.ts.map +1 -1
- package/dist/snapshot/capture.js +42 -105
- package/dist/snapshot/capture.js.map +1 -1
- package/dist/snapshot/compress.d.ts +3 -3
- package/dist/snapshot/compress.d.ts.map +1 -1
- package/dist/snapshot/compress.js +35 -67
- package/dist/snapshot/compress.js.map +1 -1
- package/dist/snapshot/storage.d.ts +7 -0
- package/dist/snapshot/storage.d.ts.map +1 -1
- package/dist/snapshot/storage.js +31 -6
- package/dist/snapshot/storage.js.map +1 -1
- package/dist/threshold/state.d.ts +5 -0
- package/dist/threshold/state.d.ts.map +1 -1
- package/dist/threshold/state.js +28 -2
- package/dist/threshold/state.js.map +1 -1
- package/dist/threshold/token-usage.d.ts +29 -0
- package/dist/threshold/token-usage.d.ts.map +1 -0
- package/dist/threshold/token-usage.js +128 -0
- package/dist/threshold/token-usage.js.map +1 -0
- package/dist/trails/identity.d.ts +76 -0
- package/dist/trails/identity.d.ts.map +1 -0
- package/dist/trails/identity.js +117 -0
- package/dist/trails/identity.js.map +1 -0
- package/dist/trails/reader.d.ts +7 -0
- package/dist/trails/reader.d.ts.map +1 -0
- package/dist/trails/reader.js +22 -0
- package/dist/trails/reader.js.map +1 -0
- package/dist/trails/writer.d.ts +9 -0
- package/dist/trails/writer.d.ts.map +1 -0
- package/dist/trails/writer.js +110 -0
- package/dist/trails/writer.js.map +1 -0
- package/dist/transcript/extractor.d.ts +17 -4
- package/dist/transcript/extractor.d.ts.map +1 -1
- package/dist/transcript/extractor.js +141 -174
- package/dist/transcript/extractor.js.map +1 -1
- package/dist/transcript/parser.d.ts.map +1 -1
- package/dist/transcript/parser.js +69 -7
- package/dist/transcript/parser.js.map +1 -1
- package/dist/types.d.ts +37 -38
- package/dist/types.d.ts.map +1 -1
- package/hooks/hooks.json +12 -14
- package/package.json +13 -10
- package/scripts/install-plugin.sh +48 -0
- package/skills/context-continuity/SKILL.md +43 -24
- package/commands/activate.md +0 -20
- package/dist/transcript/estimator.d.ts +0 -20
- package/dist/transcript/estimator.d.ts.map +0 -1
- package/dist/transcript/estimator.js +0 -95
- 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,
|
|
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
|
|
4
|
-
*
|
|
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
|
|
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":"
|
|
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"}
|