jinzd-ai-cli 0.4.74 → 0.4.75
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/{chunk-FKVJRBPO.js → chunk-3BHGEPIT.js} +1 -1
- package/dist/{chunk-BT2TCINO.js → chunk-5P4QTZBI.js} +1 -1
- package/dist/{chunk-VG3MFZYG.js → chunk-ASNDBI5R.js} +1 -1
- package/dist/{chunk-PLJUAA3J.js → chunk-C4MGON2N.js} +2 -2
- package/dist/{chunk-265H5S5H.js → chunk-E6RP5DBU.js} +1 -1
- package/dist/{chunk-D5ZDVEJJ.js → chunk-MPIUYP6Q.js} +2 -2
- package/dist/{hub-ABCHM2OR.js → hub-W3BF22UV.js} +1 -1
- package/dist/index.js +9 -9
- package/dist/{run-tests-FMEFXUGO.js → run-tests-LEYTZHPU.js} +1 -1
- package/dist/{run-tests-HBLD2R6B.js → run-tests-V2JJADIU.js} +2 -2
- package/dist/{server-NMMRIWT2.js → server-2XO72FRP.js} +20 -7
- package/dist/{task-orchestrator-24UUKJW5.js → task-orchestrator-277NWVSE.js} +3 -3
- package/dist/web/client/app.js +142 -0
- package/dist/web/client/index.html +12 -0
- package/dist/web/client/style.css +71 -0
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import {
|
|
3
3
|
schemaToJsonSchema,
|
|
4
4
|
truncateForPersist
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-MPIUYP6Q.js";
|
|
6
6
|
import {
|
|
7
7
|
AuthError,
|
|
8
8
|
ProviderError,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
MCP_PROTOCOL_VERSION,
|
|
19
19
|
MCP_TOOL_PREFIX,
|
|
20
20
|
VERSION
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-5P4QTZBI.js";
|
|
22
22
|
|
|
23
23
|
// src/providers/claude.ts
|
|
24
24
|
import Anthropic from "@anthropic-ai/sdk";
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
} from "./chunk-4BKXL7SM.js";
|
|
5
5
|
import {
|
|
6
6
|
runTestsTool
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-3BHGEPIT.js";
|
|
8
8
|
import {
|
|
9
9
|
EnvLoader,
|
|
10
10
|
NetworkError,
|
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
SUBAGENT_ALLOWED_TOOLS,
|
|
18
18
|
SUBAGENT_DEFAULT_MAX_ROUNDS,
|
|
19
19
|
SUBAGENT_MAX_ROUNDS_LIMIT
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-5P4QTZBI.js";
|
|
21
21
|
|
|
22
22
|
// src/tools/builtin/bash.ts
|
|
23
23
|
import { execSync } from "child_process";
|
|
@@ -385,7 +385,7 @@ ${content}`);
|
|
|
385
385
|
}
|
|
386
386
|
}
|
|
387
387
|
async function runTaskMode(config, providers, configManager, topic) {
|
|
388
|
-
const { TaskOrchestrator } = await import("./task-orchestrator-
|
|
388
|
+
const { TaskOrchestrator } = await import("./task-orchestrator-277NWVSE.js");
|
|
389
389
|
const orchestrator = new TaskOrchestrator(config, providers, configManager);
|
|
390
390
|
let interrupted = false;
|
|
391
391
|
const onSigint = () => {
|
package/dist/index.js
CHANGED
|
@@ -30,10 +30,10 @@ import {
|
|
|
30
30
|
saveDevState,
|
|
31
31
|
sessionHasMeaningfulContent,
|
|
32
32
|
setupProxy
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-C4MGON2N.js";
|
|
34
34
|
import {
|
|
35
35
|
ConfigManager
|
|
36
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-ASNDBI5R.js";
|
|
37
37
|
import {
|
|
38
38
|
ToolExecutor,
|
|
39
39
|
ToolRegistry,
|
|
@@ -49,11 +49,11 @@ import {
|
|
|
49
49
|
spawnAgentContext,
|
|
50
50
|
theme,
|
|
51
51
|
undoStack
|
|
52
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-MPIUYP6Q.js";
|
|
53
53
|
import {
|
|
54
54
|
fileCheckpoints
|
|
55
55
|
} from "./chunk-4BKXL7SM.js";
|
|
56
|
-
import "./chunk-
|
|
56
|
+
import "./chunk-3BHGEPIT.js";
|
|
57
57
|
import "./chunk-2ZD3YTVM.js";
|
|
58
58
|
import {
|
|
59
59
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -76,7 +76,7 @@ import {
|
|
|
76
76
|
SKILLS_DIR_NAME,
|
|
77
77
|
VERSION,
|
|
78
78
|
buildUserIdentityPrompt
|
|
79
|
-
} from "./chunk-
|
|
79
|
+
} from "./chunk-5P4QTZBI.js";
|
|
80
80
|
|
|
81
81
|
// src/index.ts
|
|
82
82
|
import { program } from "commander";
|
|
@@ -2392,7 +2392,7 @@ ${hint}` : "")
|
|
|
2392
2392
|
usage: "/test [command|filter]",
|
|
2393
2393
|
async execute(args, ctx) {
|
|
2394
2394
|
try {
|
|
2395
|
-
const { executeTests } = await import("./run-tests-
|
|
2395
|
+
const { executeTests } = await import("./run-tests-V2JJADIU.js");
|
|
2396
2396
|
const argStr = args.join(" ").trim();
|
|
2397
2397
|
let testArgs = {};
|
|
2398
2398
|
if (argStr) {
|
|
@@ -6264,7 +6264,7 @@ program.command("web").description("Start Web UI server with browser-based chat
|
|
|
6264
6264
|
console.error("Error: Invalid port number. Must be between 1 and 65535.");
|
|
6265
6265
|
process.exit(1);
|
|
6266
6266
|
}
|
|
6267
|
-
const { startWebServer } = await import("./server-
|
|
6267
|
+
const { startWebServer } = await import("./server-2XO72FRP.js");
|
|
6268
6268
|
await startWebServer({ port, host: options.host });
|
|
6269
6269
|
});
|
|
6270
6270
|
program.command("user [action] [username]").description("Manage Web UI users (list | create <name> | delete <name> | reset-password <name> | migrate <name>)").action(async (action, username) => {
|
|
@@ -6387,7 +6387,7 @@ program.command("sessions").description("List recent conversation sessions").act
|
|
|
6387
6387
|
});
|
|
6388
6388
|
program.command("batch <action> [arg] [arg2]").description("Anthropic Message Batches: submit | list | status <id> | results <id> [out] | cancel <id>").option("--dry-run", "Parse and validate input without submitting (submit only)").action(async (action, arg, arg2, options) => {
|
|
6389
6389
|
try {
|
|
6390
|
-
const batch = await import("./batch-
|
|
6390
|
+
const batch = await import("./batch-2RTTAHBL.js");
|
|
6391
6391
|
switch (action) {
|
|
6392
6392
|
case "submit":
|
|
6393
6393
|
if (!arg) {
|
|
@@ -6547,7 +6547,7 @@ program.command("hub [topic]").description("Start multi-agent hub (discuss / bra
|
|
|
6547
6547
|
}),
|
|
6548
6548
|
config.get("customProviders")
|
|
6549
6549
|
);
|
|
6550
|
-
const { startHub } = await import("./hub-
|
|
6550
|
+
const { startHub } = await import("./hub-W3BF22UV.js");
|
|
6551
6551
|
await startHub(
|
|
6552
6552
|
{
|
|
6553
6553
|
topic: topic ?? "",
|
|
@@ -20,13 +20,13 @@ import {
|
|
|
20
20
|
persistToolRound,
|
|
21
21
|
rebuildExtraMessages,
|
|
22
22
|
setupProxy
|
|
23
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-C4MGON2N.js";
|
|
24
24
|
import {
|
|
25
25
|
AuthManager
|
|
26
26
|
} from "./chunk-BYNY5JPB.js";
|
|
27
27
|
import {
|
|
28
28
|
ConfigManager
|
|
29
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-ASNDBI5R.js";
|
|
30
30
|
import {
|
|
31
31
|
ToolExecutor,
|
|
32
32
|
ToolRegistry,
|
|
@@ -44,9 +44,9 @@ import {
|
|
|
44
44
|
spawnAgentContext,
|
|
45
45
|
truncateOutput,
|
|
46
46
|
undoStack
|
|
47
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-MPIUYP6Q.js";
|
|
48
48
|
import "./chunk-4BKXL7SM.js";
|
|
49
|
-
import "./chunk-
|
|
49
|
+
import "./chunk-3BHGEPIT.js";
|
|
50
50
|
import "./chunk-2ZD3YTVM.js";
|
|
51
51
|
import {
|
|
52
52
|
AGENTIC_BEHAVIOR_GUIDELINE,
|
|
@@ -66,7 +66,7 @@ import {
|
|
|
66
66
|
SKILLS_DIR_NAME,
|
|
67
67
|
VERSION,
|
|
68
68
|
buildUserIdentityPrompt
|
|
69
|
-
} from "./chunk-
|
|
69
|
+
} from "./chunk-5P4QTZBI.js";
|
|
70
70
|
|
|
71
71
|
// src/web/server.ts
|
|
72
72
|
import express from "express";
|
|
@@ -563,6 +563,15 @@ var SessionHandler = class _SessionHandler {
|
|
|
563
563
|
models: p.info.models.map((m) => ({ id: m.id, name: m.displayName ?? m.id }))
|
|
564
564
|
}));
|
|
565
565
|
const costUsd = computeCost(this.currentProvider, this.currentModel, this.sessionTokenUsage);
|
|
566
|
+
const sess = this.sessions.current;
|
|
567
|
+
const branches = sess ? sess.listBranches().map((b) => ({
|
|
568
|
+
id: b.id,
|
|
569
|
+
title: b.title,
|
|
570
|
+
parentBranchId: b.parentBranchId,
|
|
571
|
+
parentMessageIndex: b.parentMessageIndex,
|
|
572
|
+
created: b.created.toISOString(),
|
|
573
|
+
messageCount: b.id === sess.activeBranchId ? sess.messages.length : sess.getBranchMessages(b.id)?.length ?? 0
|
|
574
|
+
})) : [];
|
|
566
575
|
this.send({
|
|
567
576
|
type: "status",
|
|
568
577
|
provider: this.currentProvider,
|
|
@@ -574,7 +583,9 @@ var SessionHandler = class _SessionHandler {
|
|
|
574
583
|
thinkingMode: this.runtimeThinking ?? false,
|
|
575
584
|
tokenUsage: { ...this.sessionTokenUsage },
|
|
576
585
|
costUsd,
|
|
577
|
-
providers: providerList
|
|
586
|
+
providers: providerList,
|
|
587
|
+
branches,
|
|
588
|
+
activeBranchId: sess?.activeBranchId ?? "main"
|
|
578
589
|
});
|
|
579
590
|
}
|
|
580
591
|
async handleMessage(raw) {
|
|
@@ -1860,6 +1871,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1860
1871
|
if (ok) {
|
|
1861
1872
|
await this.sessions.save();
|
|
1862
1873
|
this.send({ type: "info", message: `\u2713 Deleted branch "${id}"` });
|
|
1874
|
+
this.sendStatus();
|
|
1863
1875
|
} else {
|
|
1864
1876
|
this.send({ type: "error", message: `Cannot delete "${id}" (not found, active, or last remaining branch).` });
|
|
1865
1877
|
}
|
|
@@ -1876,6 +1888,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
1876
1888
|
if (ok) {
|
|
1877
1889
|
await this.sessions.save();
|
|
1878
1890
|
this.send({ type: "info", message: `\u2713 Renamed branch "${id}" \u2192 "${title}"` });
|
|
1891
|
+
this.sendStatus();
|
|
1879
1892
|
} else {
|
|
1880
1893
|
this.send({ type: "error", message: `Branch "${id}" not found.` });
|
|
1881
1894
|
}
|
|
@@ -2049,7 +2062,7 @@ ${undoResults.map((r) => ` \u2022 ${r}`).join("\n")}` });
|
|
|
2049
2062
|
case "test": {
|
|
2050
2063
|
this.send({ type: "info", message: "\u{1F9EA} Running tests..." });
|
|
2051
2064
|
try {
|
|
2052
|
-
const { executeTests } = await import("./run-tests-
|
|
2065
|
+
const { executeTests } = await import("./run-tests-V2JJADIU.js");
|
|
2053
2066
|
const argStr = args.join(" ").trim();
|
|
2054
2067
|
let testArgs = {};
|
|
2055
2068
|
if (argStr) {
|
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
getDangerLevel,
|
|
5
5
|
googleSearchContext,
|
|
6
6
|
truncateOutput
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-MPIUYP6Q.js";
|
|
8
8
|
import "./chunk-4BKXL7SM.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-3BHGEPIT.js";
|
|
10
10
|
import "./chunk-2ZD3YTVM.js";
|
|
11
11
|
import {
|
|
12
12
|
SUBAGENT_ALLOWED_TOOLS
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-5P4QTZBI.js";
|
|
14
14
|
|
|
15
15
|
// src/hub/task-orchestrator.ts
|
|
16
16
|
import { createInterface } from "readline";
|
package/dist/web/client/app.js
CHANGED
|
@@ -519,6 +519,17 @@ function handleStatus(msg) {
|
|
|
519
519
|
}
|
|
520
520
|
if (msg.tokenUsage) targetTab.tokenUsage = msg.tokenUsage;
|
|
521
521
|
|
|
522
|
+
// B2: stash branch data on the tab so tab-switch keeps the picker in sync.
|
|
523
|
+
if (Array.isArray(msg.branches)) {
|
|
524
|
+
targetTab.branches = msg.branches;
|
|
525
|
+
targetTab.activeBranchId = msg.activeBranchId || 'main';
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (isActiveTarget) {
|
|
529
|
+
// Re-render the branches panel whenever the active tab gets a status.
|
|
530
|
+
renderBranchPanel(msg.branches || [], msg.activeBranchId || 'main', msg.sessionId || '');
|
|
531
|
+
}
|
|
532
|
+
|
|
522
533
|
if (isActiveTarget) {
|
|
523
534
|
// Active tab: full UI reflection
|
|
524
535
|
btnThink.classList.toggle('btn-active-toggle', msg.thinkingMode);
|
|
@@ -1179,6 +1190,137 @@ function renderReplayStep(m, idx) {
|
|
|
1179
1190
|
</div>`;
|
|
1180
1191
|
}
|
|
1181
1192
|
|
|
1193
|
+
// ── B2: Branch sidebar panel ─────────────────────────────
|
|
1194
|
+
let _cachedBranches = [];
|
|
1195
|
+
let _cachedActiveBranchId = 'main';
|
|
1196
|
+
|
|
1197
|
+
function renderBranchPanel(branches, activeId, sessionId) {
|
|
1198
|
+
_cachedBranches = branches;
|
|
1199
|
+
_cachedActiveBranchId = activeId;
|
|
1200
|
+
const listEl = document.getElementById('branch-list');
|
|
1201
|
+
const headerEl = document.getElementById('branches-header');
|
|
1202
|
+
if (!listEl) return;
|
|
1203
|
+
|
|
1204
|
+
if (!sessionId) {
|
|
1205
|
+
if (headerEl) headerEl.textContent = 'No session';
|
|
1206
|
+
listEl.innerHTML = '<div class="text-xs opacity-40 text-center py-4">Load a session to see branches</div>';
|
|
1207
|
+
return;
|
|
1208
|
+
}
|
|
1209
|
+
if (headerEl) {
|
|
1210
|
+
headerEl.textContent = `Session ${sessionId.slice(0, 8)} · ${branches.length} branch${branches.length === 1 ? '' : 'es'}`;
|
|
1211
|
+
}
|
|
1212
|
+
if (branches.length === 0) {
|
|
1213
|
+
listEl.innerHTML = '<div class="text-xs opacity-40 text-center py-4">No branches</div>';
|
|
1214
|
+
return;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
// Build tree: compute depth by walking parent chain.
|
|
1218
|
+
const byId = Object.fromEntries(branches.map(b => [b.id, b]));
|
|
1219
|
+
function depthOf(b) {
|
|
1220
|
+
let d = 0, cur = b;
|
|
1221
|
+
while (cur && cur.parentBranchId && byId[cur.parentBranchId]) {
|
|
1222
|
+
d++;
|
|
1223
|
+
cur = byId[cur.parentBranchId];
|
|
1224
|
+
if (d > 100) break; // safety
|
|
1225
|
+
}
|
|
1226
|
+
return d;
|
|
1227
|
+
}
|
|
1228
|
+
// Depth-first order: roots first, then children in order.
|
|
1229
|
+
const ordered = [];
|
|
1230
|
+
const visited = new Set();
|
|
1231
|
+
function visit(b) {
|
|
1232
|
+
if (visited.has(b.id)) return;
|
|
1233
|
+
visited.add(b.id);
|
|
1234
|
+
ordered.push(b);
|
|
1235
|
+
for (const c of branches) {
|
|
1236
|
+
if (c.parentBranchId === b.id) visit(c);
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
for (const b of branches) {
|
|
1240
|
+
if (!b.parentBranchId || !byId[b.parentBranchId]) visit(b);
|
|
1241
|
+
}
|
|
1242
|
+
// Any remaining (orphaned) — append at end.
|
|
1243
|
+
for (const b of branches) if (!visited.has(b.id)) visit(b);
|
|
1244
|
+
|
|
1245
|
+
listEl.innerHTML = ordered.map(b => {
|
|
1246
|
+
const depth = depthOf(b);
|
|
1247
|
+
const indent = depth === 0 ? '' : '│ '.repeat(depth - 1) + '└─ ';
|
|
1248
|
+
const isActive = b.id === activeId;
|
|
1249
|
+
const marker = isActive ? '●' : '○';
|
|
1250
|
+
const parentTag = b.parentBranchId
|
|
1251
|
+
? `<span class="branch-count">← ${escapeHtml(b.parentBranchId)}@${b.parentMessageIndex}</span>`
|
|
1252
|
+
: '';
|
|
1253
|
+
return `
|
|
1254
|
+
<div class="branch-item${isActive ? ' active' : ''}" data-branch-id="${escapeHtml(b.id)}" data-branch-active="${isActive ? '1' : '0'}">
|
|
1255
|
+
<span class="branch-indent">${indent}</span>
|
|
1256
|
+
<span class="branch-marker">${marker}</span>
|
|
1257
|
+
<span class="branch-title" title="${escapeHtml(b.title)}">${escapeHtml(b.title)}</span>
|
|
1258
|
+
<span class="branch-id">${escapeHtml(b.id)}</span>
|
|
1259
|
+
<span class="branch-count">${b.messageCount}m</span>
|
|
1260
|
+
${parentTag}
|
|
1261
|
+
<span class="branch-actions">
|
|
1262
|
+
<button data-branch-action="rename" title="Rename">✎</button>
|
|
1263
|
+
<button data-branch-action="delete" title="Delete">×</button>
|
|
1264
|
+
</span>
|
|
1265
|
+
</div>`;
|
|
1266
|
+
}).join('');
|
|
1267
|
+
|
|
1268
|
+
// Wire click handlers.
|
|
1269
|
+
listEl.querySelectorAll('.branch-item').forEach(el => {
|
|
1270
|
+
el.addEventListener('click', (e) => {
|
|
1271
|
+
const actionBtn = e.target.closest('button[data-branch-action]');
|
|
1272
|
+
const id = el.dataset.branchId;
|
|
1273
|
+
if (!id) return;
|
|
1274
|
+
if (actionBtn) {
|
|
1275
|
+
e.stopPropagation();
|
|
1276
|
+
const action = actionBtn.dataset.branchAction;
|
|
1277
|
+
if (action === 'rename') {
|
|
1278
|
+
const cur = byId[id];
|
|
1279
|
+
const title = prompt('New branch title:', cur?.title || '');
|
|
1280
|
+
if (title && title.trim() && title.trim() !== cur?.title) {
|
|
1281
|
+
send({ type: 'command', name: 'branch', args: ['rename', id, title.trim()] });
|
|
1282
|
+
}
|
|
1283
|
+
} else if (action === 'delete') {
|
|
1284
|
+
if (id === activeId) {
|
|
1285
|
+
alert('Cannot delete the active branch. Switch to another branch first.');
|
|
1286
|
+
return;
|
|
1287
|
+
}
|
|
1288
|
+
if (confirm(`Delete branch "${id}"? Its messages will be lost.`)) {
|
|
1289
|
+
send({ type: 'command', name: 'branch', args: ['delete', id] });
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
// Plain click → switch branch.
|
|
1295
|
+
if (el.dataset.branchActive !== '1') {
|
|
1296
|
+
send({ type: 'command', name: 'branch', args: ['switch', id] });
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
});
|
|
1300
|
+
}
|
|
1301
|
+
|
|
1302
|
+
// "+ Fork" button — fork the active branch at its current tip.
|
|
1303
|
+
document.getElementById('btn-branch-new')?.addEventListener('click', () => {
|
|
1304
|
+
const activeTab = sessionTabs[activeTabIdx];
|
|
1305
|
+
if (!activeTab || !activeTab.sessionId) {
|
|
1306
|
+
alert('Load a session first.');
|
|
1307
|
+
return;
|
|
1308
|
+
}
|
|
1309
|
+
const activeBranch = _cachedBranches.find(b => b.id === _cachedActiveBranchId);
|
|
1310
|
+
const tip = activeBranch?.messageCount ?? 0;
|
|
1311
|
+
const raw = prompt(`Fork from message # (0–${tip}, default ${tip}):`, String(tip));
|
|
1312
|
+
if (raw === null) return;
|
|
1313
|
+
const idx = parseInt(raw.trim() || String(tip), 10);
|
|
1314
|
+
if (isNaN(idx) || idx < 0 || idx > tip) {
|
|
1315
|
+
alert(`Invalid index. Range: 0–${tip}`);
|
|
1316
|
+
return;
|
|
1317
|
+
}
|
|
1318
|
+
const title = prompt('New branch title (optional):', '');
|
|
1319
|
+
if (title === null) return;
|
|
1320
|
+
const args = ['new', String(idx), ...(title.trim() ? [title.trim()] : [])];
|
|
1321
|
+
send({ type: 'command', name: 'branch', args });
|
|
1322
|
+
});
|
|
1323
|
+
|
|
1182
1324
|
function startSessionRename(itemEl, titleEl) {
|
|
1183
1325
|
const sessionId = itemEl.dataset.sessionId;
|
|
1184
1326
|
const currentTitle = titleEl.textContent.trim();
|
|
@@ -105,6 +105,7 @@
|
|
|
105
105
|
<!-- Sidebar tabs -->
|
|
106
106
|
<div class="flex border-b border-base-content/10 flex-shrink-0">
|
|
107
107
|
<button class="sidebar-tab active flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="sessions">📋 Sessions</button>
|
|
108
|
+
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="branches" title="Conversation branches (B2)">🌿 Branches</button>
|
|
108
109
|
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="tools">🔧 Tools</button>
|
|
109
110
|
<button class="sidebar-tab flex-1 text-xs font-semibold py-2 px-1 text-center" data-tab="files">📁 Files</button>
|
|
110
111
|
</div>
|
|
@@ -127,6 +128,17 @@
|
|
|
127
128
|
<div class="text-xs opacity-40 text-center py-4">No sessions yet</div>
|
|
128
129
|
</div>
|
|
129
130
|
</div>
|
|
131
|
+
<!-- Branches tab (B2) -->
|
|
132
|
+
<div id="tab-branches" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden hidden">
|
|
133
|
+
<div class="p-2 border-b border-base-content/10 flex items-center gap-1">
|
|
134
|
+
<span class="text-xs opacity-60 flex-1 truncate" id="branches-header">No session</span>
|
|
135
|
+
<button id="btn-branch-new" class="btn btn-xs btn-primary btn-outline flex-shrink-0 whitespace-nowrap" title="Fork at current tip">+ Fork</button>
|
|
136
|
+
</div>
|
|
137
|
+
<div id="branch-list" class="flex-1 overflow-y-auto p-2 flex flex-col gap-1 text-sm">
|
|
138
|
+
<div class="text-xs opacity-40 text-center py-4">Load a session to see branches</div>
|
|
139
|
+
</div>
|
|
140
|
+
</div>
|
|
141
|
+
|
|
130
142
|
<!-- Tools tab -->
|
|
131
143
|
<div id="tab-tools" class="sidebar-tab-content flex flex-col flex-1 overflow-hidden hidden">
|
|
132
144
|
<div class="p-2 border-b border-base-content/10">
|
|
@@ -903,3 +903,74 @@ button, a, .session-item, .file-tree-row, .template-item, .tool-item, .mcp-serve
|
|
|
903
903
|
max-height: 12rem;
|
|
904
904
|
overflow-y: auto;
|
|
905
905
|
}
|
|
906
|
+
|
|
907
|
+
/* ── B2 Branch picker (sidebar) ─────────────────────────── */
|
|
908
|
+
.branch-item {
|
|
909
|
+
display: flex;
|
|
910
|
+
align-items: center;
|
|
911
|
+
gap: 0.35rem;
|
|
912
|
+
padding: 0.35rem 0.5rem;
|
|
913
|
+
border-radius: 0.35rem;
|
|
914
|
+
cursor: pointer;
|
|
915
|
+
border: 1px solid transparent;
|
|
916
|
+
transition: background 0.1s, border-color 0.1s;
|
|
917
|
+
font-size: 0.78rem;
|
|
918
|
+
line-height: 1.25;
|
|
919
|
+
position: relative;
|
|
920
|
+
}
|
|
921
|
+
.branch-item:hover {
|
|
922
|
+
background: rgba(128, 128, 128, 0.12);
|
|
923
|
+
}
|
|
924
|
+
.branch-item.active {
|
|
925
|
+
background: rgba(34, 197, 94, 0.12);
|
|
926
|
+
border-color: rgba(34, 197, 94, 0.45);
|
|
927
|
+
}
|
|
928
|
+
.branch-item .branch-marker {
|
|
929
|
+
flex-shrink: 0;
|
|
930
|
+
width: 0.8rem;
|
|
931
|
+
color: rgb(34, 197, 94);
|
|
932
|
+
}
|
|
933
|
+
.branch-item .branch-title {
|
|
934
|
+
flex: 1;
|
|
935
|
+
min-width: 0;
|
|
936
|
+
overflow: hidden;
|
|
937
|
+
text-overflow: ellipsis;
|
|
938
|
+
white-space: nowrap;
|
|
939
|
+
}
|
|
940
|
+
.branch-item .branch-id {
|
|
941
|
+
flex-shrink: 0;
|
|
942
|
+
opacity: 0.5;
|
|
943
|
+
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
944
|
+
font-size: 0.7rem;
|
|
945
|
+
}
|
|
946
|
+
.branch-item .branch-count {
|
|
947
|
+
flex-shrink: 0;
|
|
948
|
+
opacity: 0.55;
|
|
949
|
+
font-size: 0.7rem;
|
|
950
|
+
}
|
|
951
|
+
.branch-item .branch-actions {
|
|
952
|
+
display: none;
|
|
953
|
+
gap: 0.15rem;
|
|
954
|
+
flex-shrink: 0;
|
|
955
|
+
}
|
|
956
|
+
.branch-item:hover .branch-actions {
|
|
957
|
+
display: flex;
|
|
958
|
+
}
|
|
959
|
+
.branch-item .branch-actions button {
|
|
960
|
+
background: transparent;
|
|
961
|
+
border: none;
|
|
962
|
+
padding: 0 0.2rem;
|
|
963
|
+
font-size: 0.72rem;
|
|
964
|
+
cursor: pointer;
|
|
965
|
+
opacity: 0.7;
|
|
966
|
+
}
|
|
967
|
+
.branch-item .branch-actions button:hover {
|
|
968
|
+
opacity: 1;
|
|
969
|
+
}
|
|
970
|
+
.branch-item .branch-indent {
|
|
971
|
+
flex-shrink: 0;
|
|
972
|
+
color: rgba(128, 128, 128, 0.5);
|
|
973
|
+
font-family: ui-monospace, SFMono-Regular, monospace;
|
|
974
|
+
font-size: 0.72rem;
|
|
975
|
+
white-space: pre;
|
|
976
|
+
}
|