@generativereality/agentherder 0.1.3 → 0.1.4
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/dist/index.js +56 -13
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import { consola } from "consola";
|
|
|
10
10
|
import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "fs";
|
|
11
11
|
//#region package.json
|
|
12
12
|
var name = "@generativereality/agentherder";
|
|
13
|
-
var version = "0.1.
|
|
13
|
+
var version = "0.1.4";
|
|
14
14
|
var description = "Agent session manager for AI coding tools. Terminal tabs as the UI, no tmux.";
|
|
15
15
|
var package_default = {
|
|
16
16
|
name,
|
|
@@ -618,20 +618,63 @@ const resumeCommand = define({
|
|
|
618
618
|
function pathToProjectSlug(dir) {
|
|
619
619
|
return resolve(dir).replace(/\//g, "-");
|
|
620
620
|
}
|
|
621
|
-
/** Find the most recent
|
|
622
|
-
function
|
|
623
|
-
const slug = pathToProjectSlug(dir);
|
|
624
|
-
const projectDir = join(homedir(), ".claude", "projects", slug);
|
|
621
|
+
/** Find the most recent .jsonl session file in a Claude project directory */
|
|
622
|
+
function latestJsonlIn(projectDir) {
|
|
625
623
|
if (!existsSync(projectDir)) return null;
|
|
626
|
-
const
|
|
624
|
+
const files = readdirSync(projectDir).filter((f) => extname(f) === ".jsonl").map((f) => ({
|
|
627
625
|
name: f,
|
|
628
626
|
mtime: statSync(join(projectDir, f)).mtimeMs
|
|
629
627
|
})).sort((a, b) => b.mtime - a.mtime);
|
|
630
|
-
|
|
631
|
-
|
|
628
|
+
return files.length ? basename(files[0].name, ".jsonl") : null;
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Find the most recent Claude Code session ID for a directory.
|
|
632
|
+
* Also checks worktree subdirectories (.claude/worktrees/*) since tabs
|
|
633
|
+
* opened with --worktree run from a worktree path, not the repo root.
|
|
634
|
+
*/
|
|
635
|
+
function findLatestSessionId(dir) {
|
|
636
|
+
const projectsRoot = join(homedir(), ".claude", "projects");
|
|
637
|
+
const direct = latestJsonlIn(join(projectsRoot, pathToProjectSlug(dir)));
|
|
638
|
+
if (direct) return direct;
|
|
639
|
+
const worktreesDir = join(dir, ".claude", "worktrees");
|
|
640
|
+
if (existsSync(worktreesDir)) {
|
|
641
|
+
const candidates = [];
|
|
642
|
+
for (const entry of readdirSync(worktreesDir)) {
|
|
643
|
+
const projectDir = join(projectsRoot, pathToProjectSlug(join(worktreesDir, entry)));
|
|
644
|
+
const id = latestJsonlIn(projectDir);
|
|
645
|
+
if (id) {
|
|
646
|
+
const mtime = statSync(join(projectDir, id + ".jsonl")).mtimeMs;
|
|
647
|
+
candidates.push({
|
|
648
|
+
id,
|
|
649
|
+
mtime
|
|
650
|
+
});
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
if (candidates.length) {
|
|
654
|
+
candidates.sort((a, b) => b.mtime - a.mtime);
|
|
655
|
+
return candidates[0].id;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
return null;
|
|
632
659
|
}
|
|
633
660
|
//#endregion
|
|
634
661
|
//#region src/commands/fork.ts
|
|
662
|
+
/** If dir is inside .claude/worktrees/<name>, return the repo root instead */
|
|
663
|
+
function resolveSessionDir(dir) {
|
|
664
|
+
const worktreeMarker = `${join(".claude", "worktrees")}/`;
|
|
665
|
+
const idx = dir.indexOf(worktreeMarker);
|
|
666
|
+
if (idx !== -1) {
|
|
667
|
+
const repoRoot = dir.slice(0, idx - 1);
|
|
668
|
+
return {
|
|
669
|
+
sessionLookupDir: repoRoot,
|
|
670
|
+
openDir: repoRoot
|
|
671
|
+
};
|
|
672
|
+
}
|
|
673
|
+
return {
|
|
674
|
+
sessionLookupDir: dir,
|
|
675
|
+
openDir: dir
|
|
676
|
+
};
|
|
677
|
+
}
|
|
635
678
|
const forkCommand = define({
|
|
636
679
|
name: "fork",
|
|
637
680
|
description: "Fork a session into a new tab (claude --resume <id> --fork-session)",
|
|
@@ -673,16 +716,16 @@ const forkCommand = define({
|
|
|
673
716
|
consola.error(`Tab "${tabName}" has no terminal block`);
|
|
674
717
|
process.exit(1);
|
|
675
718
|
}
|
|
676
|
-
const
|
|
677
|
-
const sessionId = findLatestSessionId(
|
|
719
|
+
const { sessionLookupDir, openDir } = resolveSessionDir(termBlocks[0].meta?.["cmd:cwd"] ?? process.cwd());
|
|
720
|
+
const sessionId = findLatestSessionId(sessionLookupDir);
|
|
678
721
|
if (!sessionId) {
|
|
679
|
-
consola.error(`No Claude session found for ${
|
|
680
|
-
consola.info(`Looked in ~/.claude/projects/${pathToProjectSlug(
|
|
722
|
+
consola.error(`No Claude session found for ${sessionLookupDir}`);
|
|
723
|
+
consola.info(`Looked in ~/.claude/projects/${pathToProjectSlug(sessionLookupDir)}/`);
|
|
681
724
|
process.exit(1);
|
|
682
725
|
}
|
|
683
726
|
const newTabId = await openSession({
|
|
684
727
|
tabName: newName,
|
|
685
|
-
dir:
|
|
728
|
+
dir: openDir,
|
|
686
729
|
claudeCmd: `claude --resume ${sessionId} --fork-session`
|
|
687
730
|
});
|
|
688
731
|
consola.success(`Forked "${tabName}" → "${newName}" [${newTabId.slice(0, 8)}]`);
|