sessionlog 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +388 -0
- package/dist/agent/agents/claude-code.d.ts +76 -0
- package/dist/agent/agents/claude-code.d.ts.map +1 -0
- package/dist/agent/agents/claude-code.js +769 -0
- package/dist/agent/agents/claude-code.js.map +1 -0
- package/dist/agent/agents/cursor.d.ts +35 -0
- package/dist/agent/agents/cursor.d.ts.map +1 -0
- package/dist/agent/agents/cursor.js +294 -0
- package/dist/agent/agents/cursor.js.map +1 -0
- package/dist/agent/agents/gemini-cli.d.ts +62 -0
- package/dist/agent/agents/gemini-cli.d.ts.map +1 -0
- package/dist/agent/agents/gemini-cli.js +474 -0
- package/dist/agent/agents/gemini-cli.js.map +1 -0
- package/dist/agent/agents/opencode.d.ts +100 -0
- package/dist/agent/agents/opencode.d.ts.map +1 -0
- package/dist/agent/agents/opencode.js +423 -0
- package/dist/agent/agents/opencode.js.map +1 -0
- package/dist/agent/registry.d.ts +54 -0
- package/dist/agent/registry.d.ts.map +1 -0
- package/dist/agent/registry.js +123 -0
- package/dist/agent/registry.js.map +1 -0
- package/dist/agent/session-types.d.ts +45 -0
- package/dist/agent/session-types.d.ts.map +1 -0
- package/dist/agent/session-types.js +48 -0
- package/dist/agent/session-types.js.map +1 -0
- package/dist/agent/types.d.ts +126 -0
- package/dist/agent/types.d.ts.map +1 -0
- package/dist/agent/types.js +40 -0
- package/dist/agent/types.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +425 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/clean.d.ts +30 -0
- package/dist/commands/clean.d.ts.map +1 -0
- package/dist/commands/clean.js +98 -0
- package/dist/commands/clean.js.map +1 -0
- package/dist/commands/disable.d.ts +23 -0
- package/dist/commands/disable.d.ts.map +1 -0
- package/dist/commands/disable.js +57 -0
- package/dist/commands/disable.js.map +1 -0
- package/dist/commands/doctor.d.ts +43 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +97 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/enable.d.ts +37 -0
- package/dist/commands/enable.d.ts.map +1 -0
- package/dist/commands/enable.js +133 -0
- package/dist/commands/enable.js.map +1 -0
- package/dist/commands/explain.d.ts +68 -0
- package/dist/commands/explain.d.ts.map +1 -0
- package/dist/commands/explain.js +182 -0
- package/dist/commands/explain.js.map +1 -0
- package/dist/commands/reset.d.ts +23 -0
- package/dist/commands/reset.d.ts.map +1 -0
- package/dist/commands/reset.js +68 -0
- package/dist/commands/reset.js.map +1 -0
- package/dist/commands/resume.d.ts +42 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +133 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/rewind.d.ts +34 -0
- package/dist/commands/rewind.d.ts.map +1 -0
- package/dist/commands/rewind.js +155 -0
- package/dist/commands/rewind.js.map +1 -0
- package/dist/commands/status.d.ts +51 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +112 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/config.d.ts +40 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +127 -0
- package/dist/config.js.map +1 -0
- package/dist/git-operations.d.ts +191 -0
- package/dist/git-operations.d.ts.map +1 -0
- package/dist/git-operations.js +462 -0
- package/dist/git-operations.js.map +1 -0
- package/dist/hooks/git-hooks.d.ts +22 -0
- package/dist/hooks/git-hooks.d.ts.map +1 -0
- package/dist/hooks/git-hooks.js +139 -0
- package/dist/hooks/git-hooks.js.map +1 -0
- package/dist/hooks/lifecycle.d.ts +21 -0
- package/dist/hooks/lifecycle.d.ts.map +1 -0
- package/dist/hooks/lifecycle.js +179 -0
- package/dist/hooks/lifecycle.js.map +1 -0
- package/dist/index.d.ts +76 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +166 -0
- package/dist/index.js.map +1 -0
- package/dist/security/redaction.d.ts +35 -0
- package/dist/security/redaction.d.ts.map +1 -0
- package/dist/security/redaction.js +239 -0
- package/dist/security/redaction.js.map +1 -0
- package/dist/session/state-machine.d.ts +90 -0
- package/dist/session/state-machine.d.ts.map +1 -0
- package/dist/session/state-machine.js +345 -0
- package/dist/session/state-machine.js.map +1 -0
- package/dist/store/checkpoint-store.d.ts +59 -0
- package/dist/store/checkpoint-store.d.ts.map +1 -0
- package/dist/store/checkpoint-store.js +321 -0
- package/dist/store/checkpoint-store.js.map +1 -0
- package/dist/store/native-store.d.ts +14 -0
- package/dist/store/native-store.d.ts.map +1 -0
- package/dist/store/native-store.js +159 -0
- package/dist/store/native-store.js.map +1 -0
- package/dist/store/provider-types.d.ts +78 -0
- package/dist/store/provider-types.d.ts.map +1 -0
- package/dist/store/provider-types.js +12 -0
- package/dist/store/provider-types.js.map +1 -0
- package/dist/store/session-store.d.ts +36 -0
- package/dist/store/session-store.d.ts.map +1 -0
- package/dist/store/session-store.js +193 -0
- package/dist/store/session-store.js.map +1 -0
- package/dist/strategy/attribution.d.ts +39 -0
- package/dist/strategy/attribution.d.ts.map +1 -0
- package/dist/strategy/attribution.js +225 -0
- package/dist/strategy/attribution.js.map +1 -0
- package/dist/strategy/common.d.ts +57 -0
- package/dist/strategy/common.d.ts.map +1 -0
- package/dist/strategy/common.js +156 -0
- package/dist/strategy/common.js.map +1 -0
- package/dist/strategy/content-overlap.d.ts +33 -0
- package/dist/strategy/content-overlap.d.ts.map +1 -0
- package/dist/strategy/content-overlap.js +176 -0
- package/dist/strategy/content-overlap.js.map +1 -0
- package/dist/strategy/manual-commit.d.ts +36 -0
- package/dist/strategy/manual-commit.d.ts.map +1 -0
- package/dist/strategy/manual-commit.js +717 -0
- package/dist/strategy/manual-commit.js.map +1 -0
- package/dist/strategy/types.d.ts +163 -0
- package/dist/strategy/types.d.ts.map +1 -0
- package/dist/strategy/types.js +48 -0
- package/dist/strategy/types.js.map +1 -0
- package/dist/summarize/claude-generator.d.ts +25 -0
- package/dist/summarize/claude-generator.d.ts.map +1 -0
- package/dist/summarize/claude-generator.js +87 -0
- package/dist/summarize/claude-generator.js.map +1 -0
- package/dist/summarize/summarize.d.ts +52 -0
- package/dist/summarize/summarize.d.ts.map +1 -0
- package/dist/summarize/summarize.js +335 -0
- package/dist/summarize/summarize.js.map +1 -0
- package/dist/types.d.ts +293 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +94 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/chunk-files.d.ts +25 -0
- package/dist/utils/chunk-files.d.ts.map +1 -0
- package/dist/utils/chunk-files.js +47 -0
- package/dist/utils/chunk-files.js.map +1 -0
- package/dist/utils/commit-message.d.ts +11 -0
- package/dist/utils/commit-message.d.ts.map +1 -0
- package/dist/utils/commit-message.js +54 -0
- package/dist/utils/commit-message.js.map +1 -0
- package/dist/utils/detect-agent.d.ts +19 -0
- package/dist/utils/detect-agent.d.ts.map +1 -0
- package/dist/utils/detect-agent.js +34 -0
- package/dist/utils/detect-agent.js.map +1 -0
- package/dist/utils/hook-managers.d.ts +24 -0
- package/dist/utils/hook-managers.d.ts.map +1 -0
- package/dist/utils/hook-managers.js +96 -0
- package/dist/utils/hook-managers.js.map +1 -0
- package/dist/utils/ide-tags.d.ts +12 -0
- package/dist/utils/ide-tags.d.ts.map +1 -0
- package/dist/utils/ide-tags.js +30 -0
- package/dist/utils/ide-tags.js.map +1 -0
- package/dist/utils/paths.d.ts +32 -0
- package/dist/utils/paths.d.ts.map +1 -0
- package/dist/utils/paths.js +55 -0
- package/dist/utils/paths.js.map +1 -0
- package/dist/utils/preview-rewind.d.ts +23 -0
- package/dist/utils/preview-rewind.d.ts.map +1 -0
- package/dist/utils/preview-rewind.js +63 -0
- package/dist/utils/preview-rewind.js.map +1 -0
- package/dist/utils/rewind-conflict.d.ts +52 -0
- package/dist/utils/rewind-conflict.d.ts.map +1 -0
- package/dist/utils/rewind-conflict.js +79 -0
- package/dist/utils/rewind-conflict.js.map +1 -0
- package/dist/utils/shadow-branch.d.ts +44 -0
- package/dist/utils/shadow-branch.d.ts.map +1 -0
- package/dist/utils/shadow-branch.js +93 -0
- package/dist/utils/shadow-branch.js.map +1 -0
- package/dist/utils/string-utils.d.ts +24 -0
- package/dist/utils/string-utils.d.ts.map +1 -0
- package/dist/utils/string-utils.js +47 -0
- package/dist/utils/string-utils.js.map +1 -0
- package/dist/utils/todo-extract.d.ts +52 -0
- package/dist/utils/todo-extract.d.ts.map +1 -0
- package/dist/utils/todo-extract.js +167 -0
- package/dist/utils/todo-extract.js.map +1 -0
- package/dist/utils/trailers.d.ts +36 -0
- package/dist/utils/trailers.d.ts.map +1 -0
- package/dist/utils/trailers.js +148 -0
- package/dist/utils/trailers.js.map +1 -0
- package/dist/utils/transcript-parse.d.ts +57 -0
- package/dist/utils/transcript-parse.d.ts.map +1 -0
- package/dist/utils/transcript-parse.js +126 -0
- package/dist/utils/transcript-parse.js.map +1 -0
- package/dist/utils/transcript-timestamp.d.ts +22 -0
- package/dist/utils/transcript-timestamp.d.ts.map +1 -0
- package/dist/utils/transcript-timestamp.js +56 -0
- package/dist/utils/transcript-timestamp.js.map +1 -0
- package/dist/utils/tree-ops.d.ts +47 -0
- package/dist/utils/tree-ops.d.ts.map +1 -0
- package/dist/utils/tree-ops.js +145 -0
- package/dist/utils/tree-ops.js.map +1 -0
- package/dist/utils/tty.d.ts +25 -0
- package/dist/utils/tty.d.ts.map +1 -0
- package/dist/utils/tty.js +70 -0
- package/dist/utils/tty.js.map +1 -0
- package/dist/utils/validation.d.ts +31 -0
- package/dist/utils/validation.d.ts.map +1 -0
- package/dist/utils/validation.js +59 -0
- package/dist/utils/validation.js.map +1 -0
- package/dist/utils/worktree.d.ts +16 -0
- package/dist/utils/worktree.d.ts.map +1 -0
- package/dist/utils/worktree.js +50 -0
- package/dist/utils/worktree.js.map +1 -0
- package/package.json +64 -0
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewind Preview
|
|
3
|
+
*
|
|
4
|
+
* Preview what files would change when rewinding to a checkpoint.
|
|
5
|
+
* Shows files that would be restored and untracked files that would be deleted.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: strategy/manual_commit_rewind.go
|
|
8
|
+
*/
|
|
9
|
+
import { getHead, lsTree, getUntrackedFiles } from '../git-operations.js';
|
|
10
|
+
import { isInfrastructurePath } from './paths.js';
|
|
11
|
+
/**
|
|
12
|
+
* Preview what will happen when rewinding to the given commit.
|
|
13
|
+
*
|
|
14
|
+
* Returns the list of files that would be restored from the checkpoint
|
|
15
|
+
* and untracked files that would be deleted.
|
|
16
|
+
*
|
|
17
|
+
* For logs-only rewind points, returns empty arrays since the working
|
|
18
|
+
* directory is not modified.
|
|
19
|
+
*/
|
|
20
|
+
export async function previewRewind(commitHash, isLogsOnly, preservedUntrackedFiles, cwd) {
|
|
21
|
+
// Logs-only points don't modify the working directory
|
|
22
|
+
if (isLogsOnly) {
|
|
23
|
+
return { filesToRestore: [], filesToDelete: [] };
|
|
24
|
+
}
|
|
25
|
+
// Get files in the checkpoint tree (excluding infrastructure)
|
|
26
|
+
const checkpointEntries = await lsTree(commitHash, undefined, cwd);
|
|
27
|
+
const checkpointFiles = new Set();
|
|
28
|
+
const filesToRestore = [];
|
|
29
|
+
for (const entry of checkpointEntries) {
|
|
30
|
+
if (!isInfrastructurePath(entry.name)) {
|
|
31
|
+
checkpointFiles.add(entry.name);
|
|
32
|
+
filesToRestore.push(entry.name);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// Get HEAD tree to identify tracked files
|
|
36
|
+
const headHash = await getHead(cwd);
|
|
37
|
+
const headEntries = await lsTree(headHash, undefined, cwd);
|
|
38
|
+
const trackedFiles = new Set();
|
|
39
|
+
for (const entry of headEntries) {
|
|
40
|
+
trackedFiles.add(entry.name);
|
|
41
|
+
}
|
|
42
|
+
// Build set of preserved untracked files
|
|
43
|
+
const preserved = new Set(preservedUntrackedFiles ?? []);
|
|
44
|
+
// Find untracked files that would be deleted
|
|
45
|
+
const untrackedNow = await getUntrackedFiles(cwd);
|
|
46
|
+
const filesToDelete = [];
|
|
47
|
+
for (const relPath of untrackedNow) {
|
|
48
|
+
// Skip if file exists in the checkpoint
|
|
49
|
+
if (checkpointFiles.has(relPath))
|
|
50
|
+
continue;
|
|
51
|
+
// Skip if file is tracked in HEAD
|
|
52
|
+
if (trackedFiles.has(relPath))
|
|
53
|
+
continue;
|
|
54
|
+
// Skip if file was preserved from session start
|
|
55
|
+
if (preserved.has(relPath))
|
|
56
|
+
continue;
|
|
57
|
+
filesToDelete.push(relPath);
|
|
58
|
+
}
|
|
59
|
+
filesToRestore.sort();
|
|
60
|
+
filesToDelete.sort();
|
|
61
|
+
return { filesToRestore, filesToDelete };
|
|
62
|
+
}
|
|
63
|
+
//# sourceMappingURL=preview-rewind.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preview-rewind.js","sourceRoot":"","sources":["../../src/utils/preview-rewind.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AAC1E,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAOlD;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB,EAClB,UAAmB,EACnB,uBAAkC,EAClC,GAAY;IAEZ,sDAAsD;IACtD,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,EAAE,cAAc,EAAE,EAAE,EAAE,aAAa,EAAE,EAAE,EAAE,CAAC;IACnD,CAAC;IAED,8DAA8D;IAC9D,MAAM,iBAAiB,GAAG,MAAM,MAAM,CAAC,UAAU,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IACnE,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAC1C,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,KAAK,MAAM,KAAK,IAAI,iBAAiB,EAAE,CAAC;QACtC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAChC,cAAc,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAClC,CAAC;IACH,CAAC;IAED,0CAA0C;IAC1C,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,QAAQ,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,YAAY,GAAG,IAAI,GAAG,EAAU,CAAC;IACvC,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE,CAAC;QAChC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC/B,CAAC;IAED,yCAAyC;IACzC,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,uBAAuB,IAAI,EAAE,CAAC,CAAC;IAEzD,6CAA6C;IAC7C,MAAM,YAAY,GAAG,MAAM,iBAAiB,CAAC,GAAG,CAAC,CAAC;IAClD,MAAM,aAAa,GAAa,EAAE,CAAC;IAEnC,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;QACnC,wCAAwC;QACxC,IAAI,eAAe,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAC3C,kCAAkC;QAClC,IAAI,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QACxC,gDAAgD;QAChD,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,SAAS;QAErC,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IAC9B,CAAC;IAED,cAAc,CAAC,IAAI,EAAE,CAAC;IACtB,aAAa,CAAC,IAAI,EAAE,CAAC;IAErB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewind Conflict Detection
|
|
3
|
+
*
|
|
4
|
+
* Utilities for detecting timestamp conflicts when restoring session logs
|
|
5
|
+
* from checkpoints. Prevents silent overwrites of newer local transcripts.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: strategy/manual_commit_rewind.go
|
|
8
|
+
*/
|
|
9
|
+
export declare const enum SessionRestoreStatus {
|
|
10
|
+
/** Local file doesn't exist */
|
|
11
|
+
New = 0,
|
|
12
|
+
/** Local and checkpoint are the same */
|
|
13
|
+
Unchanged = 1,
|
|
14
|
+
/** Checkpoint has newer entries */
|
|
15
|
+
CheckpointNewer = 2,
|
|
16
|
+
/** Local has newer entries (conflict) */
|
|
17
|
+
LocalNewer = 3
|
|
18
|
+
}
|
|
19
|
+
export interface SessionRestoreInfo {
|
|
20
|
+
sessionID: string;
|
|
21
|
+
prompt: string;
|
|
22
|
+
status: SessionRestoreStatus;
|
|
23
|
+
localTime: Date;
|
|
24
|
+
checkpointTime: Date;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Determine the restore status based on local and checkpoint timestamps.
|
|
28
|
+
*/
|
|
29
|
+
export declare function classifyTimestamps(localTime: Date | null, checkpointTime: Date | null): SessionRestoreStatus;
|
|
30
|
+
/**
|
|
31
|
+
* Returns a human-readable status string.
|
|
32
|
+
*/
|
|
33
|
+
export declare function statusToText(status: SessionRestoreStatus): string;
|
|
34
|
+
/**
|
|
35
|
+
* Check if any sessions have conflicts (local is newer than checkpoint).
|
|
36
|
+
*/
|
|
37
|
+
export declare function hasConflicts(sessions: SessionRestoreInfo[]): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Separate sessions into conflicting and non-conflicting lists.
|
|
40
|
+
*/
|
|
41
|
+
export declare function partitionConflicts(sessions: SessionRestoreInfo[]): {
|
|
42
|
+
conflicting: SessionRestoreInfo[];
|
|
43
|
+
nonConflicting: SessionRestoreInfo[];
|
|
44
|
+
};
|
|
45
|
+
import type { Agent } from '../agent/types.js';
|
|
46
|
+
import type { AgentType } from '../types.js';
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the agent from checkpoint metadata agent type string.
|
|
49
|
+
* Returns null if the agent type is not registered.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveAgentForRewind(agentType: AgentType): Agent | null;
|
|
52
|
+
//# sourceMappingURL=rewind-conflict.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewind-conflict.d.ts","sourceRoot":"","sources":["../../src/utils/rewind-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,0BAAkB,oBAAoB;IACpC,+BAA+B;IAC/B,GAAG,IAAI;IACP,wCAAwC;IACxC,SAAS,IAAI;IACb,mCAAmC;IACnC,eAAe,IAAI;IACnB,yCAAyC;IACzC,UAAU,IAAI;CACf;AAED,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,oBAAoB,CAAC;IAC7B,SAAS,EAAE,IAAI,CAAC;IAChB,cAAc,EAAE,IAAI,CAAC;CACtB;AAMD;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,IAAI,GAAG,IAAI,EACtB,cAAc,EAAE,IAAI,GAAG,IAAI,GAC1B,oBAAoB,CAkBtB;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,oBAAoB,GAAG,MAAM,CAajE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAEpE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG;IAClE,WAAW,EAAE,kBAAkB,EAAE,CAAC;IAClC,cAAc,EAAE,kBAAkB,EAAE,CAAC;CACtC,CAWA;AAMD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE/C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,SAAS,GAAG,KAAK,GAAG,IAAI,CAExE"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Rewind Conflict Detection
|
|
3
|
+
*
|
|
4
|
+
* Utilities for detecting timestamp conflicts when restoring session logs
|
|
5
|
+
* from checkpoints. Prevents silent overwrites of newer local transcripts.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: strategy/manual_commit_rewind.go
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Classification
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Determine the restore status based on local and checkpoint timestamps.
|
|
14
|
+
*/
|
|
15
|
+
export function classifyTimestamps(localTime, checkpointTime) {
|
|
16
|
+
// Local file doesn't exist
|
|
17
|
+
if (!localTime || localTime.getTime() === 0) {
|
|
18
|
+
return 0 /* SessionRestoreStatus.New */;
|
|
19
|
+
}
|
|
20
|
+
// Can't determine checkpoint time
|
|
21
|
+
if (!checkpointTime || checkpointTime.getTime() === 0) {
|
|
22
|
+
return 0 /* SessionRestoreStatus.New */;
|
|
23
|
+
}
|
|
24
|
+
if (localTime.getTime() > checkpointTime.getTime()) {
|
|
25
|
+
return 3 /* SessionRestoreStatus.LocalNewer */;
|
|
26
|
+
}
|
|
27
|
+
if (checkpointTime.getTime() > localTime.getTime()) {
|
|
28
|
+
return 2 /* SessionRestoreStatus.CheckpointNewer */;
|
|
29
|
+
}
|
|
30
|
+
return 1 /* SessionRestoreStatus.Unchanged */;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Returns a human-readable status string.
|
|
34
|
+
*/
|
|
35
|
+
export function statusToText(status) {
|
|
36
|
+
switch (status) {
|
|
37
|
+
case 0 /* SessionRestoreStatus.New */:
|
|
38
|
+
return '(new)';
|
|
39
|
+
case 1 /* SessionRestoreStatus.Unchanged */:
|
|
40
|
+
return '(unchanged)';
|
|
41
|
+
case 2 /* SessionRestoreStatus.CheckpointNewer */:
|
|
42
|
+
return '(checkpoint is newer)';
|
|
43
|
+
case 3 /* SessionRestoreStatus.LocalNewer */:
|
|
44
|
+
return '(local is newer)';
|
|
45
|
+
default:
|
|
46
|
+
return '';
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Check if any sessions have conflicts (local is newer than checkpoint).
|
|
51
|
+
*/
|
|
52
|
+
export function hasConflicts(sessions) {
|
|
53
|
+
return sessions.some((s) => s.status === 3 /* SessionRestoreStatus.LocalNewer */);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Separate sessions into conflicting and non-conflicting lists.
|
|
57
|
+
*/
|
|
58
|
+
export function partitionConflicts(sessions) {
|
|
59
|
+
const conflicting = [];
|
|
60
|
+
const nonConflicting = [];
|
|
61
|
+
for (const s of sessions) {
|
|
62
|
+
if (s.status === 3 /* SessionRestoreStatus.LocalNewer */) {
|
|
63
|
+
conflicting.push(s);
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
nonConflicting.push(s);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { conflicting, nonConflicting };
|
|
70
|
+
}
|
|
71
|
+
import { getAgentByType } from '../agent/registry.js';
|
|
72
|
+
/**
|
|
73
|
+
* Resolve the agent from checkpoint metadata agent type string.
|
|
74
|
+
* Returns null if the agent type is not registered.
|
|
75
|
+
*/
|
|
76
|
+
export function resolveAgentForRewind(agentType) {
|
|
77
|
+
return getAgentByType(agentType);
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=rewind-conflict.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rewind-conflict.js","sourceRoot":"","sources":["../../src/utils/rewind-conflict.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAyBH,+EAA+E;AAC/E,iBAAiB;AACjB,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAsB,EACtB,cAA2B;IAE3B,2BAA2B;IAC3B,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QAC5C,wCAAgC;IAClC,CAAC;IAED,kCAAkC;IAClC,IAAI,CAAC,cAAc,IAAI,cAAc,CAAC,OAAO,EAAE,KAAK,CAAC,EAAE,CAAC;QACtD,wCAAgC;IAClC,CAAC;IAED,IAAI,SAAS,CAAC,OAAO,EAAE,GAAG,cAAc,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,+CAAuC;IACzC,CAAC;IACD,IAAI,cAAc,CAAC,OAAO,EAAE,GAAG,SAAS,CAAC,OAAO,EAAE,EAAE,CAAC;QACnD,oDAA4C;IAC9C,CAAC;IACD,8CAAsC;AACxC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,MAA4B;IACvD,QAAQ,MAAM,EAAE,CAAC;QACf;YACE,OAAO,OAAO,CAAC;QACjB;YACE,OAAO,aAAa,CAAC;QACvB;YACE,OAAO,uBAAuB,CAAC;QACjC;YACE,OAAO,kBAAkB,CAAC;QAC5B;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAA8B;IACzD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,4CAAoC,CAAC,CAAC;AAC5E,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,QAA8B;IAI/D,MAAM,WAAW,GAAyB,EAAE,CAAC;IAC7C,MAAM,cAAc,GAAyB,EAAE,CAAC;IAChD,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,IAAI,CAAC,CAAC,MAAM,4CAAoC,EAAE,CAAC;YACjD,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,CAAC;aAAM,CAAC;YACN,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;IACH,CAAC;IACD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,CAAC;AACzC,CAAC;AAOD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAoB;IACxD,OAAO,cAAc,CAAC,SAAS,CAAC,CAAC;AACnC,CAAC"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow Branch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Standalone utilities for shadow branch name generation, parsing,
|
|
5
|
+
* and classification. Extracted from checkpoint-store.ts for reuse.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: checkpoint/temporary.go, strategy/cleanup.go
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Hash a worktree identifier to a short hex string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function hashWorktreeID(worktreeID: string): string;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the shadow branch name for a base commit hash and worktree identifier.
|
|
15
|
+
* Format: entire/<commit[:7]>-<hash(worktreeID)[:6]>
|
|
16
|
+
*/
|
|
17
|
+
export declare function shadowBranchNameForCommit(baseCommit: string, worktreeID?: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Parse a shadow branch name to extract commit prefix and worktree hash.
|
|
20
|
+
* Returns null if the branch name doesn't match the shadow branch pattern.
|
|
21
|
+
*/
|
|
22
|
+
export declare function parseShadowBranchName(branchName: string): {
|
|
23
|
+
commitPrefix: string;
|
|
24
|
+
worktreeHash: string;
|
|
25
|
+
} | null;
|
|
26
|
+
/**
|
|
27
|
+
* Returns true if the branch name matches the shadow branch pattern.
|
|
28
|
+
* The "entire/checkpoints/v1" branch is NOT a shadow branch.
|
|
29
|
+
*/
|
|
30
|
+
export declare function isShadowBranch(branchName: string): boolean;
|
|
31
|
+
/**
|
|
32
|
+
* List all shadow branches in the repository.
|
|
33
|
+
* Returns an empty array if no shadow branches exist.
|
|
34
|
+
*/
|
|
35
|
+
export declare function listShadowBranches(cwd?: string): Promise<string[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Delete the specified shadow branches from the repository.
|
|
38
|
+
* Returns two arrays: successfully deleted and failed to delete.
|
|
39
|
+
*/
|
|
40
|
+
export declare function deleteShadowBranches(branches: string[], cwd?: string): Promise<{
|
|
41
|
+
deleted: string[];
|
|
42
|
+
failed: string[];
|
|
43
|
+
}>;
|
|
44
|
+
//# sourceMappingURL=shadow-branch.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadow-branch.d.ts","sourceRoot":"","sources":["../../src/utils/shadow-branch.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAcH;;GAEG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAMzD;AAED;;;GAGG;AACH,wBAAgB,yBAAyB,CAAC,UAAU,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAOzF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,UAAU,EAAE,MAAM,GACjB;IAAE,YAAY,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAgBvD;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAGxE;AAED;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,MAAM,EAAE,EAClB,GAAG,CAAC,EAAE,MAAM,GACX,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,EAAE,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAclD"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shadow Branch Utilities
|
|
3
|
+
*
|
|
4
|
+
* Standalone utilities for shadow branch name generation, parsing,
|
|
5
|
+
* and classification. Extracted from checkpoint-store.ts for reuse.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: checkpoint/temporary.go, strategy/cleanup.go
|
|
8
|
+
*/
|
|
9
|
+
import * as crypto from 'node:crypto';
|
|
10
|
+
import { SHADOW_BRANCH_PREFIX, SHADOW_BRANCH_HASH_LENGTH, CHECKPOINTS_BRANCH } from '../types.js';
|
|
11
|
+
import { listBranches, deleteBranch } from '../git-operations.js';
|
|
12
|
+
/** Worktree ID hash length (6 hex characters) */
|
|
13
|
+
const WORKTREE_ID_HASH_LENGTH = 6;
|
|
14
|
+
/** Pattern for shadow branches: entire/<hex7+>(-<hex6+>)? */
|
|
15
|
+
const SHADOW_BRANCH_PATTERN = new RegExp(`^${SHADOW_BRANCH_PREFIX.replace('/', '\\/')}[0-9a-f]{${SHADOW_BRANCH_HASH_LENGTH},}(-[0-9a-f]{${WORKTREE_ID_HASH_LENGTH},})?$`);
|
|
16
|
+
/**
|
|
17
|
+
* Hash a worktree identifier to a short hex string.
|
|
18
|
+
*/
|
|
19
|
+
export function hashWorktreeID(worktreeID) {
|
|
20
|
+
return crypto
|
|
21
|
+
.createHash('sha256')
|
|
22
|
+
.update(worktreeID)
|
|
23
|
+
.digest('hex')
|
|
24
|
+
.slice(0, WORKTREE_ID_HASH_LENGTH);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Returns the shadow branch name for a base commit hash and worktree identifier.
|
|
28
|
+
* Format: entire/<commit[:7]>-<hash(worktreeID)[:6]>
|
|
29
|
+
*/
|
|
30
|
+
export function shadowBranchNameForCommit(baseCommit, worktreeID) {
|
|
31
|
+
const commitPart = baseCommit.slice(0, SHADOW_BRANCH_HASH_LENGTH);
|
|
32
|
+
if (worktreeID) {
|
|
33
|
+
const worktreeHash = hashWorktreeID(worktreeID);
|
|
34
|
+
return `${SHADOW_BRANCH_PREFIX}${commitPart}-${worktreeHash}`;
|
|
35
|
+
}
|
|
36
|
+
return `${SHADOW_BRANCH_PREFIX}${commitPart}`;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Parse a shadow branch name to extract commit prefix and worktree hash.
|
|
40
|
+
* Returns null if the branch name doesn't match the shadow branch pattern.
|
|
41
|
+
*/
|
|
42
|
+
export function parseShadowBranchName(branchName) {
|
|
43
|
+
if (!branchName.startsWith(SHADOW_BRANCH_PREFIX))
|
|
44
|
+
return null;
|
|
45
|
+
if (branchName === CHECKPOINTS_BRANCH)
|
|
46
|
+
return null;
|
|
47
|
+
const suffix = branchName.slice(SHADOW_BRANCH_PREFIX.length);
|
|
48
|
+
const lastDash = suffix.lastIndexOf('-');
|
|
49
|
+
if (lastDash === -1 || lastDash === 0 || lastDash === suffix.length - 1) {
|
|
50
|
+
// No dash or dash at boundary — old format with just commit prefix
|
|
51
|
+
return { commitPrefix: suffix, worktreeHash: '' };
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
commitPrefix: suffix.slice(0, lastDash),
|
|
55
|
+
worktreeHash: suffix.slice(lastDash + 1),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns true if the branch name matches the shadow branch pattern.
|
|
60
|
+
* The "entire/checkpoints/v1" branch is NOT a shadow branch.
|
|
61
|
+
*/
|
|
62
|
+
export function isShadowBranch(branchName) {
|
|
63
|
+
if (branchName === CHECKPOINTS_BRANCH)
|
|
64
|
+
return false;
|
|
65
|
+
return SHADOW_BRANCH_PATTERN.test(branchName);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* List all shadow branches in the repository.
|
|
69
|
+
* Returns an empty array if no shadow branches exist.
|
|
70
|
+
*/
|
|
71
|
+
export async function listShadowBranches(cwd) {
|
|
72
|
+
const allBranches = await listBranches(cwd);
|
|
73
|
+
return allBranches.filter(isShadowBranch);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Delete the specified shadow branches from the repository.
|
|
77
|
+
* Returns two arrays: successfully deleted and failed to delete.
|
|
78
|
+
*/
|
|
79
|
+
export async function deleteShadowBranches(branches, cwd) {
|
|
80
|
+
const deleted = [];
|
|
81
|
+
const failed = [];
|
|
82
|
+
for (const branch of branches) {
|
|
83
|
+
try {
|
|
84
|
+
await deleteBranch(branch, false, cwd);
|
|
85
|
+
deleted.push(branch);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
failed.push(branch);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return { deleted, failed };
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=shadow-branch.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shadow-branch.js","sourceRoot":"","sources":["../../src/utils/shadow-branch.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Utilities
|
|
3
|
+
*
|
|
4
|
+
* Rune-safe string operations for multi-byte character handling.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Truncate a string to a maximum number of Unicode code points (runes).
|
|
8
|
+
* Avoids splitting multi-byte UTF-8 characters.
|
|
9
|
+
*/
|
|
10
|
+
export declare function truncateRunes(s: string, maxRunes: number, suffix?: string): string;
|
|
11
|
+
/**
|
|
12
|
+
* Collapse all whitespace sequences (including newlines) to single spaces.
|
|
13
|
+
*/
|
|
14
|
+
export declare function collapseWhitespace(s: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Capitalize the first character of a string.
|
|
17
|
+
*/
|
|
18
|
+
export declare function capitalizeFirst(s: string): string;
|
|
19
|
+
/**
|
|
20
|
+
* Count lines in a string.
|
|
21
|
+
* Empty string = 0 lines. String without newline = 1 line.
|
|
22
|
+
*/
|
|
23
|
+
export declare function countLines(content: string): number;
|
|
24
|
+
//# sourceMappingURL=string-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-utils.d.ts","sourceRoot":"","sources":["../../src/utils/string-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,GAAE,MAAc,GAAG,MAAM,CAMzF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAGjD;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAQlD"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* String Utilities
|
|
3
|
+
*
|
|
4
|
+
* Rune-safe string operations for multi-byte character handling.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Truncate a string to a maximum number of Unicode code points (runes).
|
|
8
|
+
* Avoids splitting multi-byte UTF-8 characters.
|
|
9
|
+
*/
|
|
10
|
+
export function truncateRunes(s, maxRunes, suffix = '...') {
|
|
11
|
+
const runes = [...s];
|
|
12
|
+
if (runes.length <= maxRunes) {
|
|
13
|
+
return s;
|
|
14
|
+
}
|
|
15
|
+
return runes.slice(0, maxRunes).join('') + suffix;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Collapse all whitespace sequences (including newlines) to single spaces.
|
|
19
|
+
*/
|
|
20
|
+
export function collapseWhitespace(s) {
|
|
21
|
+
return s.replace(/\s+/g, ' ').trim();
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Capitalize the first character of a string.
|
|
25
|
+
*/
|
|
26
|
+
export function capitalizeFirst(s) {
|
|
27
|
+
if (s.length === 0)
|
|
28
|
+
return s;
|
|
29
|
+
return s[0].toUpperCase() + s.slice(1);
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Count lines in a string.
|
|
33
|
+
* Empty string = 0 lines. String without newline = 1 line.
|
|
34
|
+
*/
|
|
35
|
+
export function countLines(content) {
|
|
36
|
+
if (content === '')
|
|
37
|
+
return 0;
|
|
38
|
+
let lines = 0;
|
|
39
|
+
for (let i = 0; i < content.length; i++) {
|
|
40
|
+
if (content[i] === '\n')
|
|
41
|
+
lines++;
|
|
42
|
+
}
|
|
43
|
+
if (!content.endsWith('\n'))
|
|
44
|
+
lines++;
|
|
45
|
+
return lines;
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=string-utils.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"string-utils.js","sourceRoot":"","sources":["../../src/utils/string-utils.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAC,CAAS,EAAE,QAAgB,EAAE,SAAiB,KAAK;IAC/E,MAAM,KAAK,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACrB,IAAI,KAAK,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;QAC7B,OAAO,CAAC,CAAC;IACX,CAAC;IACD,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC;AACpD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,CAAS;IAC1C,OAAO,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;AACvC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,CAAS;IACvC,IAAI,CAAC,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AACzC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,UAAU,CAAC,OAAe;IACxC,IAAI,OAAO,KAAK,EAAE;QAAE,OAAO,CAAC,CAAC;IAC7B,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,IAAI,OAAO,CAAC,CAAC,CAAC,KAAK,IAAI;YAAE,KAAK,EAAE,CAAC;IACnC,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;QAAE,KAAK,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Todo Extraction
|
|
3
|
+
*
|
|
4
|
+
* Parses TodoWrite tool_input JSON to extract todo content for
|
|
5
|
+
* incremental checkpoint commit messages.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: strategy/messages.go
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Extract the content of the last completed todo item from a todos JSON array.
|
|
11
|
+
* Returns the work that was just finished (used for commit messages).
|
|
12
|
+
*
|
|
13
|
+
* Returns empty string if no completed items exist or JSON is invalid.
|
|
14
|
+
*/
|
|
15
|
+
export declare function extractLastCompletedTodo(todosJSON: string | Buffer): string;
|
|
16
|
+
/**
|
|
17
|
+
* Return the number of todo items in the JSON array.
|
|
18
|
+
* Returns 0 if the JSON is invalid or empty.
|
|
19
|
+
*/
|
|
20
|
+
export declare function countTodos(todosJSON: string | Buffer): number;
|
|
21
|
+
/**
|
|
22
|
+
* Extract the content of the in-progress todo item from tool_input.
|
|
23
|
+
* Used for commit messages in incremental checkpoints.
|
|
24
|
+
*
|
|
25
|
+
* Priority order:
|
|
26
|
+
* 1. in_progress item (current work)
|
|
27
|
+
* 2. first pending item (next work - fallback)
|
|
28
|
+
* 3. last completed item (work just finished)
|
|
29
|
+
* 4. first item with unknown status (edge case)
|
|
30
|
+
* 5. empty string (no items)
|
|
31
|
+
*/
|
|
32
|
+
export declare function extractInProgressTodo(todosJSON: string | Buffer): string;
|
|
33
|
+
/**
|
|
34
|
+
* Extract todo content from a TodoWrite tool_input object.
|
|
35
|
+
* Handles unwrapping the outer { todos: [...] } structure.
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractTodoContentFromToolInput(toolInput: unknown): string;
|
|
38
|
+
/**
|
|
39
|
+
* Extract last completed todo from a TodoWrite tool_input object.
|
|
40
|
+
*/
|
|
41
|
+
export declare function extractLastCompletedTodoFromToolInput(toolInput: unknown): string;
|
|
42
|
+
/**
|
|
43
|
+
* Count todos from a TodoWrite tool_input object.
|
|
44
|
+
*/
|
|
45
|
+
export declare function countTodosFromToolInput(toolInput: unknown): number;
|
|
46
|
+
/**
|
|
47
|
+
* Format a commit message for an incremental checkpoint.
|
|
48
|
+
* Format: "<todo-content> (<tool-use-id>)"
|
|
49
|
+
* Fallback: "Checkpoint #<sequence>: <tool-use-id>"
|
|
50
|
+
*/
|
|
51
|
+
export declare function formatIncrementalMessage(todoContent: string, sequence: number, toolUseID: string): string;
|
|
52
|
+
//# sourceMappingURL=todo-extract.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todo-extract.d.ts","sourceRoot":"","sources":["../../src/utils/todo-extract.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAgBH;;;;;GAKG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAW3E;AAED;;;GAGG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAG7D;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CA+BxE;AAMD;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAI1E;AAED;;GAEG;AACH,wBAAgB,qCAAqC,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAIhF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,CAIlE;AASD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,GAChB,MAAM,CAOR"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Todo Extraction
|
|
3
|
+
*
|
|
4
|
+
* Parses TodoWrite tool_input JSON to extract todo content for
|
|
5
|
+
* incremental checkpoint commit messages.
|
|
6
|
+
*
|
|
7
|
+
* Ported from Go: strategy/messages.go
|
|
8
|
+
*/
|
|
9
|
+
// ============================================================================
|
|
10
|
+
// Extraction Functions
|
|
11
|
+
// ============================================================================
|
|
12
|
+
/**
|
|
13
|
+
* Extract the content of the last completed todo item from a todos JSON array.
|
|
14
|
+
* Returns the work that was just finished (used for commit messages).
|
|
15
|
+
*
|
|
16
|
+
* Returns empty string if no completed items exist or JSON is invalid.
|
|
17
|
+
*/
|
|
18
|
+
export function extractLastCompletedTodo(todosJSON) {
|
|
19
|
+
const todos = parseTodos(todosJSON);
|
|
20
|
+
if (!todos)
|
|
21
|
+
return '';
|
|
22
|
+
let lastCompleted = '';
|
|
23
|
+
for (const todo of todos) {
|
|
24
|
+
if (todo.status === 'completed') {
|
|
25
|
+
lastCompleted = todo.content;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
return lastCompleted;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Return the number of todo items in the JSON array.
|
|
32
|
+
* Returns 0 if the JSON is invalid or empty.
|
|
33
|
+
*/
|
|
34
|
+
export function countTodos(todosJSON) {
|
|
35
|
+
const todos = parseTodos(todosJSON);
|
|
36
|
+
return todos ? todos.length : 0;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Extract the content of the in-progress todo item from tool_input.
|
|
40
|
+
* Used for commit messages in incremental checkpoints.
|
|
41
|
+
*
|
|
42
|
+
* Priority order:
|
|
43
|
+
* 1. in_progress item (current work)
|
|
44
|
+
* 2. first pending item (next work - fallback)
|
|
45
|
+
* 3. last completed item (work just finished)
|
|
46
|
+
* 4. first item with unknown status (edge case)
|
|
47
|
+
* 5. empty string (no items)
|
|
48
|
+
*/
|
|
49
|
+
export function extractInProgressTodo(todosJSON) {
|
|
50
|
+
const todos = parseTodos(todosJSON);
|
|
51
|
+
if (!todos || todos.length === 0)
|
|
52
|
+
return '';
|
|
53
|
+
// 1. in_progress item
|
|
54
|
+
for (const todo of todos) {
|
|
55
|
+
if (todo.status === 'in_progress') {
|
|
56
|
+
return todo.content;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// 2. first pending item
|
|
60
|
+
for (const todo of todos) {
|
|
61
|
+
if (todo.status === 'pending') {
|
|
62
|
+
return todo.content;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 3. last completed item
|
|
66
|
+
let lastCompleted = '';
|
|
67
|
+
for (const todo of todos) {
|
|
68
|
+
if (todo.status === 'completed') {
|
|
69
|
+
lastCompleted = todo.content;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (lastCompleted)
|
|
73
|
+
return lastCompleted;
|
|
74
|
+
// 4. first item with content (unknown status edge case)
|
|
75
|
+
if (todos[0].content)
|
|
76
|
+
return todos[0].content;
|
|
77
|
+
return '';
|
|
78
|
+
}
|
|
79
|
+
// ============================================================================
|
|
80
|
+
// Tool Input Wrappers
|
|
81
|
+
// ============================================================================
|
|
82
|
+
/**
|
|
83
|
+
* Extract todo content from a TodoWrite tool_input object.
|
|
84
|
+
* Handles unwrapping the outer { todos: [...] } structure.
|
|
85
|
+
*/
|
|
86
|
+
export function extractTodoContentFromToolInput(toolInput) {
|
|
87
|
+
const todosJSON = extractTodosArrayJSON(toolInput);
|
|
88
|
+
if (!todosJSON)
|
|
89
|
+
return '';
|
|
90
|
+
return extractInProgressTodo(todosJSON);
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Extract last completed todo from a TodoWrite tool_input object.
|
|
94
|
+
*/
|
|
95
|
+
export function extractLastCompletedTodoFromToolInput(toolInput) {
|
|
96
|
+
const todosJSON = extractTodosArrayJSON(toolInput);
|
|
97
|
+
if (!todosJSON)
|
|
98
|
+
return '';
|
|
99
|
+
return extractLastCompletedTodo(todosJSON);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Count todos from a TodoWrite tool_input object.
|
|
103
|
+
*/
|
|
104
|
+
export function countTodosFromToolInput(toolInput) {
|
|
105
|
+
const todosJSON = extractTodosArrayJSON(toolInput);
|
|
106
|
+
if (!todosJSON)
|
|
107
|
+
return 0;
|
|
108
|
+
return countTodos(todosJSON);
|
|
109
|
+
}
|
|
110
|
+
// ============================================================================
|
|
111
|
+
// Formatting
|
|
112
|
+
// ============================================================================
|
|
113
|
+
/** Maximum length for descriptions in commit messages */
|
|
114
|
+
const MAX_DESCRIPTION_LENGTH = 60;
|
|
115
|
+
/**
|
|
116
|
+
* Format a commit message for an incremental checkpoint.
|
|
117
|
+
* Format: "<todo-content> (<tool-use-id>)"
|
|
118
|
+
* Fallback: "Checkpoint #<sequence>: <tool-use-id>"
|
|
119
|
+
*/
|
|
120
|
+
export function formatIncrementalMessage(todoContent, sequence, toolUseID) {
|
|
121
|
+
if (!todoContent) {
|
|
122
|
+
return `Checkpoint #${sequence}: ${toolUseID}`;
|
|
123
|
+
}
|
|
124
|
+
const truncated = truncateDescription(todoContent, MAX_DESCRIPTION_LENGTH);
|
|
125
|
+
return `${truncated} (${toolUseID})`;
|
|
126
|
+
}
|
|
127
|
+
// ============================================================================
|
|
128
|
+
// Internal Helpers
|
|
129
|
+
// ============================================================================
|
|
130
|
+
function parseTodos(todosJSON) {
|
|
131
|
+
try {
|
|
132
|
+
const str = typeof todosJSON === 'string' ? todosJSON : todosJSON.toString('utf-8');
|
|
133
|
+
if (!str)
|
|
134
|
+
return null;
|
|
135
|
+
const parsed = JSON.parse(str);
|
|
136
|
+
if (!Array.isArray(parsed))
|
|
137
|
+
return null;
|
|
138
|
+
return parsed;
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return null;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
function extractTodosArrayJSON(toolInput) {
|
|
145
|
+
if (!toolInput || typeof toolInput !== 'object')
|
|
146
|
+
return null;
|
|
147
|
+
const input = toolInput;
|
|
148
|
+
const todos = input.todos;
|
|
149
|
+
if (!Array.isArray(todos))
|
|
150
|
+
return null;
|
|
151
|
+
try {
|
|
152
|
+
return JSON.stringify(todos);
|
|
153
|
+
}
|
|
154
|
+
catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
function truncateDescription(s, maxLen) {
|
|
159
|
+
const runes = [...s];
|
|
160
|
+
if (runes.length <= maxLen)
|
|
161
|
+
return s;
|
|
162
|
+
const suffix = '...';
|
|
163
|
+
const suffixRunes = [...suffix];
|
|
164
|
+
const truncateAt = Math.max(0, maxLen - suffixRunes.length);
|
|
165
|
+
return runes.slice(0, truncateAt).join('') + suffix;
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=todo-extract.js.map
|