@generativereality/cctabs 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/.claude-plugin/plugin.json +1 -1
- package/dist/index.js +75 -23
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cctabs",
|
|
3
3
|
"description": "Claude Code tab manager. Terminal tabs as the UI, no tmux. Claude can orchestrate its own sibling sessions.",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.4",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "generativereality",
|
|
7
7
|
"url": "https://cctabs.com"
|
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/cctabs";
|
|
13
|
-
var version = "0.1.
|
|
13
|
+
var version = "0.1.4";
|
|
14
14
|
var description = "Claude Code tab manager. Terminal tabs as the UI, no tmux.";
|
|
15
15
|
var package_default = {
|
|
16
16
|
name,
|
|
@@ -202,14 +202,17 @@ var WaveAdapter = class {
|
|
|
202
202
|
this.jwt = process.env.WAVETERM_JWT ?? "";
|
|
203
203
|
}
|
|
204
204
|
blocksList() {
|
|
205
|
+
const wsId = process.env.WAVETERM_WORKSPACEID ?? "";
|
|
206
|
+
const args = [
|
|
207
|
+
"blocks",
|
|
208
|
+
"list",
|
|
209
|
+
"--json",
|
|
210
|
+
"--timeout",
|
|
211
|
+
"15000"
|
|
212
|
+
];
|
|
213
|
+
if (wsId) args.push("--workspace", wsId);
|
|
205
214
|
try {
|
|
206
|
-
const out = execFileSync("wsh",
|
|
207
|
-
"blocks",
|
|
208
|
-
"list",
|
|
209
|
-
"--json",
|
|
210
|
-
"--timeout",
|
|
211
|
-
"15000"
|
|
212
|
-
], { encoding: "utf-8" });
|
|
215
|
+
const out = execFileSync("wsh", args, { encoding: "utf-8" });
|
|
213
216
|
return JSON.parse(out);
|
|
214
217
|
} catch {
|
|
215
218
|
return [];
|
|
@@ -325,27 +328,37 @@ var WaveAdapter = class {
|
|
|
325
328
|
tabsById.set(b.tabid, arr);
|
|
326
329
|
}
|
|
327
330
|
const tabNames = /* @__PURE__ */ new Map();
|
|
328
|
-
let
|
|
331
|
+
let rawWorkspaces = [];
|
|
329
332
|
try {
|
|
330
333
|
for (const tabId of tabsById.keys()) {
|
|
331
334
|
const td = await this.getTab(tabId);
|
|
332
335
|
tabNames.set(tabId, td.name ?? tabId.slice(0, 8));
|
|
333
336
|
}
|
|
334
|
-
|
|
337
|
+
rawWorkspaces = await this.workspaceList();
|
|
335
338
|
} catch {} finally {
|
|
336
339
|
this.closeSocket();
|
|
337
340
|
}
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
|
|
341
|
+
const currentWsId = process.env.WAVETERM_WORKSPACEID ?? "";
|
|
342
|
+
const tabIdsHere = [...tabsById.keys()];
|
|
343
|
+
const existing = rawWorkspaces.find((w) => w.workspacedata.oid === currentWsId);
|
|
344
|
+
const workspaces = [];
|
|
345
|
+
if (currentWsId) workspaces.push({
|
|
346
|
+
workspacedata: {
|
|
347
|
+
oid: currentWsId,
|
|
348
|
+
name: existing?.workspacedata.name ?? (currentWsId.slice(0, 8) || "current"),
|
|
349
|
+
tabids: tabIdsHere
|
|
350
|
+
},
|
|
351
|
+
windowid: existing?.windowid ?? ""
|
|
352
|
+
});
|
|
353
|
+
for (const ws of rawWorkspaces) if (ws.workspacedata.oid !== currentWsId) workspaces.push(ws);
|
|
354
|
+
if (!workspaces.length) workspaces.push({
|
|
355
|
+
workspacedata: {
|
|
356
|
+
oid: "",
|
|
357
|
+
name: "default",
|
|
358
|
+
tabids: tabIdsHere
|
|
359
|
+
},
|
|
360
|
+
windowid: ""
|
|
361
|
+
});
|
|
349
362
|
return {
|
|
350
363
|
blocks,
|
|
351
364
|
tabsById,
|
|
@@ -856,6 +869,39 @@ function listSessionNames(dir) {
|
|
|
856
869
|
}
|
|
857
870
|
return results.sort((a, b) => b.mtime - a.mtime);
|
|
858
871
|
}
|
|
872
|
+
/**
|
|
873
|
+
* Resolve a session ID prefix (e.g. "19aae7b4") to the full UUID by scanning
|
|
874
|
+
* `~/.claude/projects/`. Returns the input unchanged if it already looks like
|
|
875
|
+
* a full UUID, or null if no unique match exists. Pass `dir` to scope the
|
|
876
|
+
* search to one project; otherwise every project is checked.
|
|
877
|
+
*
|
|
878
|
+
* `claude --resume <prefix>` does NOT accept truncated IDs — it treats them
|
|
879
|
+
* as a search query and shows the picker. So callers must expand prefixes
|
|
880
|
+
* before forwarding to claude.
|
|
881
|
+
*/
|
|
882
|
+
function expandSessionId(input, dir) {
|
|
883
|
+
if (!input) return null;
|
|
884
|
+
if (/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(input)) return input;
|
|
885
|
+
const projectsRoot = join(homedir(), ".claude", "projects");
|
|
886
|
+
if (!existsSync(projectsRoot)) return null;
|
|
887
|
+
const projectDirs = dir ? [join(projectsRoot, pathToProjectSlug(dir))] : readdirSync(projectsRoot).map((d) => join(projectsRoot, d)).filter((p) => {
|
|
888
|
+
try {
|
|
889
|
+
return statSync(p).isDirectory();
|
|
890
|
+
} catch {
|
|
891
|
+
return false;
|
|
892
|
+
}
|
|
893
|
+
});
|
|
894
|
+
const matches = [];
|
|
895
|
+
for (const pd of projectDirs) {
|
|
896
|
+
if (!existsSync(pd)) continue;
|
|
897
|
+
for (const f of readdirSync(pd)) {
|
|
898
|
+
if (extname(f) !== ".jsonl") continue;
|
|
899
|
+
const id = basename(f, ".jsonl");
|
|
900
|
+
if (id.startsWith(input) && !matches.includes(id)) matches.push(id);
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
return matches.length === 1 ? matches[0] : null;
|
|
904
|
+
}
|
|
859
905
|
//#endregion
|
|
860
906
|
//#region src/commands/resume.ts
|
|
861
907
|
function formatAge(mtimeMs) {
|
|
@@ -897,8 +943,14 @@ const resumeCommand = define({
|
|
|
897
943
|
}
|
|
898
944
|
const explicitSession = ctx.values.session;
|
|
899
945
|
let sessionId;
|
|
900
|
-
if (explicitSession)
|
|
901
|
-
|
|
946
|
+
if (explicitSession) {
|
|
947
|
+
const expanded = expandSessionId(explicitSession, dir) ?? expandSessionId(explicitSession);
|
|
948
|
+
if (!expanded) {
|
|
949
|
+
consola.error(`Session '${explicitSession}' not found (or matches multiple sessions). Pass the full UUID.`);
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
sessionId = expanded;
|
|
953
|
+
} else {
|
|
902
954
|
const sessions = findSessionsByName(dir, name);
|
|
903
955
|
if (sessions.length === 0) {
|
|
904
956
|
consola.error(`No session named "${name}" in ${dir}`);
|