sidekick-agent-hub 0.13.5 → 0.13.6
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/README.md +2 -0
- package/dist/sidekick-cli.mjs +358 -204
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ Requires **Node.js 20+**.
|
|
|
25
25
|
3. The dashboard auto-detects your project and session provider
|
|
26
26
|
4. Press `?` to see all keybindings
|
|
27
27
|
|
|
28
|
+
> **OpenCode note:** OpenCode session monitoring reads `opencode.db` and currently expects an executable `sqlite3` runtime in the host environment.
|
|
29
|
+
|
|
28
30
|
## Usage
|
|
29
31
|
|
|
30
32
|
```bash
|
package/dist/sidekick-cli.mjs
CHANGED
|
@@ -4323,6 +4323,11 @@ var require_detect = __commonJS({
|
|
|
4323
4323
|
const xdg = process.env.XDG_DATA_HOME;
|
|
4324
4324
|
if (xdg)
|
|
4325
4325
|
return path7.join(xdg, "opencode");
|
|
4326
|
+
if (process.platform === "darwin")
|
|
4327
|
+
return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
|
|
4328
|
+
if (process.platform === "win32") {
|
|
4329
|
+
return path7.join(process.env.LOCALAPPDATA || process.env.APPDATA || path7.join(os5.homedir(), "AppData", "Local"), "opencode");
|
|
4330
|
+
}
|
|
4326
4331
|
return path7.join(os5.homedir(), ".local", "share", "opencode");
|
|
4327
4332
|
}
|
|
4328
4333
|
function getCodexHome() {
|
|
@@ -6125,29 +6130,42 @@ var require_openCodeDatabase = __commonJS({
|
|
|
6125
6130
|
var child_process_1 = __require("child_process");
|
|
6126
6131
|
var OpenCodeDatabase = class {
|
|
6127
6132
|
dbPath;
|
|
6128
|
-
|
|
6133
|
+
runtimeStatus = null;
|
|
6129
6134
|
constructor(dataDir) {
|
|
6130
6135
|
this.dbPath = path7.join(dataDir, "opencode.db");
|
|
6131
6136
|
}
|
|
6132
6137
|
isAvailable() {
|
|
6133
6138
|
return fs9.existsSync(this.dbPath);
|
|
6134
6139
|
}
|
|
6140
|
+
getRuntimeStatus() {
|
|
6141
|
+
if (this.runtimeStatus)
|
|
6142
|
+
return this.runtimeStatus;
|
|
6143
|
+
if (!this.isAvailable()) {
|
|
6144
|
+
this.runtimeStatus = { available: false, kind: "db_missing" };
|
|
6145
|
+
return this.runtimeStatus;
|
|
6146
|
+
}
|
|
6147
|
+
return { available: false, kind: "query_failed", message: "OpenCode database has not been initialized yet." };
|
|
6148
|
+
}
|
|
6135
6149
|
open() {
|
|
6136
|
-
if (this.
|
|
6137
|
-
return this.
|
|
6150
|
+
if (this.runtimeStatus)
|
|
6151
|
+
return this.runtimeStatus.available;
|
|
6152
|
+
if (!this.isAvailable()) {
|
|
6153
|
+
this.runtimeStatus = { available: false, kind: "db_missing" };
|
|
6154
|
+
return false;
|
|
6155
|
+
}
|
|
6138
6156
|
try {
|
|
6139
6157
|
(0, child_process_1.execFileSync)("sqlite3", ["--version"], { encoding: "utf-8", timeout: 3e3, stdio: ["pipe", "pipe", "pipe"] });
|
|
6140
|
-
this.
|
|
6158
|
+
this.runtimeStatus = { available: true, kind: "available" };
|
|
6141
6159
|
return true;
|
|
6142
|
-
} catch {
|
|
6143
|
-
this.
|
|
6160
|
+
} catch (error) {
|
|
6161
|
+
this.runtimeStatus = toRuntimeStatus(error);
|
|
6144
6162
|
return false;
|
|
6145
6163
|
}
|
|
6146
6164
|
}
|
|
6147
6165
|
close() {
|
|
6148
6166
|
}
|
|
6149
6167
|
query(sql, params = []) {
|
|
6150
|
-
if (!this.
|
|
6168
|
+
if (!this.open())
|
|
6151
6169
|
return [];
|
|
6152
6170
|
let query = sql;
|
|
6153
6171
|
for (const param of params) {
|
|
@@ -6165,11 +6183,13 @@ var require_openCodeDatabase = __commonJS({
|
|
|
6165
6183
|
stdio: ["pipe", "pipe", "pipe"],
|
|
6166
6184
|
maxBuffer: 50 * 1024 * 1024
|
|
6167
6185
|
});
|
|
6186
|
+
this.runtimeStatus = { available: true, kind: "available" };
|
|
6168
6187
|
const trimmed = result.trim();
|
|
6169
6188
|
if (!trimmed)
|
|
6170
6189
|
return [];
|
|
6171
6190
|
return JSON.parse(trimmed);
|
|
6172
|
-
} catch {
|
|
6191
|
+
} catch (error) {
|
|
6192
|
+
this.runtimeStatus = toRuntimeStatus(error, "query_failed");
|
|
6173
6193
|
return [];
|
|
6174
6194
|
}
|
|
6175
6195
|
}
|
|
@@ -6179,27 +6199,49 @@ var require_openCodeDatabase = __commonJS({
|
|
|
6179
6199
|
}
|
|
6180
6200
|
findProjectByWorktree(workspacePath) {
|
|
6181
6201
|
const normalized = normalizePath(workspacePath);
|
|
6182
|
-
const
|
|
6183
|
-
|
|
6184
|
-
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6189
|
-
|
|
6190
|
-
|
|
6191
|
-
|
|
6192
|
-
|
|
6202
|
+
const all = this.getAllProjects();
|
|
6203
|
+
let best = null;
|
|
6204
|
+
let bestScore = -1;
|
|
6205
|
+
for (const project of all) {
|
|
6206
|
+
const worktreeScore = pathMatchScore(project.worktree, normalized);
|
|
6207
|
+
if (worktreeScore > bestScore) {
|
|
6208
|
+
best = project;
|
|
6209
|
+
bestScore = worktreeScore;
|
|
6210
|
+
}
|
|
6211
|
+
for (const sandbox of project.sandboxes) {
|
|
6212
|
+
const sandboxScore = pathMatchScore(sandbox, normalized);
|
|
6213
|
+
if (sandboxScore > bestScore) {
|
|
6214
|
+
best = project;
|
|
6215
|
+
bestScore = sandboxScore;
|
|
6216
|
+
}
|
|
6193
6217
|
}
|
|
6194
6218
|
}
|
|
6195
|
-
|
|
6196
|
-
|
|
6197
|
-
|
|
6219
|
+
return bestScore >= 0 ? best : null;
|
|
6220
|
+
}
|
|
6221
|
+
findProjectBySessionDirectory(workspacePath) {
|
|
6222
|
+
const normalized = normalizePath(workspacePath);
|
|
6223
|
+
const sessions = this.query("SELECT id, project_id, title, directory, time_created, time_updated FROM session WHERE parent_id IS NULL ORDER BY time_updated DESC");
|
|
6224
|
+
let bestProjectId = null;
|
|
6225
|
+
let bestScore = -1;
|
|
6226
|
+
for (const session of sessions) {
|
|
6227
|
+
const score = pathMatchScore(session.directory, normalized);
|
|
6228
|
+
if (score > bestScore) {
|
|
6229
|
+
bestProjectId = session.project_id;
|
|
6230
|
+
bestScore = score;
|
|
6231
|
+
}
|
|
6198
6232
|
}
|
|
6199
|
-
|
|
6233
|
+
if (!bestProjectId)
|
|
6234
|
+
return null;
|
|
6235
|
+
return this.getAllProjects().find((project) => project.id === bestProjectId) ?? null;
|
|
6200
6236
|
}
|
|
6201
6237
|
getAllProjects() {
|
|
6202
|
-
return this.query("SELECT id, worktree, name, time_created, time_updated FROM project")
|
|
6238
|
+
return this.query("SELECT id, worktree, name, sandboxes, time_created, time_updated FROM project").map((project) => ({
|
|
6239
|
+
...project,
|
|
6240
|
+
sandboxes: parseStringArray(project.sandboxes)
|
|
6241
|
+
}));
|
|
6242
|
+
}
|
|
6243
|
+
hasProject(projectId) {
|
|
6244
|
+
return this.queryOne("SELECT id FROM project WHERE id = ? LIMIT 1", [projectId]) !== null;
|
|
6203
6245
|
}
|
|
6204
6246
|
getSessionsForProject(projectId) {
|
|
6205
6247
|
return this.query("SELECT id, project_id, title, directory, time_created, time_updated FROM session WHERE project_id = ? AND parent_id IS NULL ORDER BY time_updated DESC", [projectId]);
|
|
@@ -6312,6 +6354,48 @@ var require_openCodeDatabase = __commonJS({
|
|
|
6312
6354
|
return path7.resolve(input);
|
|
6313
6355
|
}
|
|
6314
6356
|
}
|
|
6357
|
+
function parseStringArray(value) {
|
|
6358
|
+
if (!value)
|
|
6359
|
+
return [];
|
|
6360
|
+
try {
|
|
6361
|
+
const parsed = JSON.parse(value);
|
|
6362
|
+
return Array.isArray(parsed) ? parsed.filter((entry) => typeof entry === "string" && entry.length > 0).map(normalizePath) : [];
|
|
6363
|
+
} catch {
|
|
6364
|
+
return [];
|
|
6365
|
+
}
|
|
6366
|
+
}
|
|
6367
|
+
function normalizeForCompare(input) {
|
|
6368
|
+
const normalized = normalizePath(input);
|
|
6369
|
+
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
6370
|
+
}
|
|
6371
|
+
function pathMatchScore(candidate, workspacePath) {
|
|
6372
|
+
if (!candidate)
|
|
6373
|
+
return -1;
|
|
6374
|
+
const normalizedCandidate = normalizeForCompare(candidate);
|
|
6375
|
+
const normalizedWorkspace = normalizeForCompare(workspacePath);
|
|
6376
|
+
if (normalizedCandidate === normalizedWorkspace)
|
|
6377
|
+
return 1e4 + normalizedCandidate.length;
|
|
6378
|
+
const candidatePrefix = normalizedCandidate.endsWith(path7.sep) ? normalizedCandidate : normalizedCandidate + path7.sep;
|
|
6379
|
+
const workspacePrefix = normalizedWorkspace.endsWith(path7.sep) ? normalizedWorkspace : normalizedWorkspace + path7.sep;
|
|
6380
|
+
if (normalizedWorkspace.startsWith(candidatePrefix))
|
|
6381
|
+
return 5e3 + normalizedCandidate.length;
|
|
6382
|
+
if (normalizedCandidate.startsWith(workspacePrefix))
|
|
6383
|
+
return 1e3 + normalizedCandidate.length;
|
|
6384
|
+
return -1;
|
|
6385
|
+
}
|
|
6386
|
+
function toRuntimeStatus(error, fallback = "sqlite_missing") {
|
|
6387
|
+
if (isErrno(error, "ENOENT")) {
|
|
6388
|
+
return { available: false, kind: "sqlite_missing", message: "sqlite3 executable not found in PATH." };
|
|
6389
|
+
}
|
|
6390
|
+
if (isErrno(error, "EPERM") || isErrno(error, "EACCES")) {
|
|
6391
|
+
return { available: false, kind: "sqlite_blocked", message: "sqlite3 exists but could not be executed." };
|
|
6392
|
+
}
|
|
6393
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
6394
|
+
return { available: false, kind: fallback, message };
|
|
6395
|
+
}
|
|
6396
|
+
function isErrno(error, code) {
|
|
6397
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === code;
|
|
6398
|
+
}
|
|
6315
6399
|
}
|
|
6316
6400
|
});
|
|
6317
6401
|
|
|
@@ -6369,6 +6453,12 @@ var require_openCode = __commonJS({
|
|
|
6369
6453
|
if (xdg) {
|
|
6370
6454
|
return path7.join(xdg, "opencode");
|
|
6371
6455
|
}
|
|
6456
|
+
if (process.platform === "darwin") {
|
|
6457
|
+
return path7.join(os5.homedir(), "Library", "Application Support", "opencode");
|
|
6458
|
+
}
|
|
6459
|
+
if (process.platform === "win32") {
|
|
6460
|
+
return path7.join(process.env.LOCALAPPDATA || process.env.APPDATA || path7.join(os5.homedir(), "AppData", "Local"), "opencode");
|
|
6461
|
+
}
|
|
6372
6462
|
return path7.join(os5.homedir(), ".local", "share", "opencode");
|
|
6373
6463
|
}
|
|
6374
6464
|
function getStorageDir() {
|
|
@@ -6435,11 +6525,17 @@ var require_openCode = __commonJS({
|
|
|
6435
6525
|
return null;
|
|
6436
6526
|
}
|
|
6437
6527
|
}
|
|
6438
|
-
function resolveProjectId(workspacePath, db) {
|
|
6528
|
+
function resolveProjectId(workspacePath, db, runtimeStatus) {
|
|
6439
6529
|
if (db) {
|
|
6440
6530
|
const dbProject = db.findProjectByWorktree(workspacePath);
|
|
6441
6531
|
if (dbProject)
|
|
6442
6532
|
return dbProject.id;
|
|
6533
|
+
const byDirectory = db.findProjectBySessionDirectory(workspacePath);
|
|
6534
|
+
if (byDirectory)
|
|
6535
|
+
return byDirectory.id;
|
|
6536
|
+
}
|
|
6537
|
+
if (runtimeStatus.kind !== "db_missing") {
|
|
6538
|
+
return null;
|
|
6443
6539
|
}
|
|
6444
6540
|
return resolveProjectIdFromFiles(workspacePath);
|
|
6445
6541
|
}
|
|
@@ -6774,6 +6870,7 @@ var require_openCode = __commonJS({
|
|
|
6774
6870
|
displayName = "OpenCode";
|
|
6775
6871
|
db = null;
|
|
6776
6872
|
dbInitialized = false;
|
|
6873
|
+
dbStatus = { available: false, kind: "db_missing" };
|
|
6777
6874
|
/** Cache of session metadata populated during listing */
|
|
6778
6875
|
sessionMetaCache = /* @__PURE__ */ new Map();
|
|
6779
6876
|
/** Lazy-initialize the database connection. */
|
|
@@ -6783,41 +6880,80 @@ var require_openCode = __commonJS({
|
|
|
6783
6880
|
this.dbInitialized = true;
|
|
6784
6881
|
const dataDir = getOpenCodeDataDir();
|
|
6785
6882
|
const db = new openCodeDatabase_1.OpenCodeDatabase(dataDir);
|
|
6786
|
-
if (db.
|
|
6883
|
+
if (db.open()) {
|
|
6787
6884
|
this.db = db;
|
|
6788
6885
|
}
|
|
6886
|
+
this.dbStatus = db.getRuntimeStatus();
|
|
6789
6887
|
return this.db;
|
|
6790
6888
|
}
|
|
6889
|
+
getRuntimeStatus() {
|
|
6890
|
+
const db = this.ensureDb();
|
|
6891
|
+
if (db)
|
|
6892
|
+
return db.getRuntimeStatus();
|
|
6893
|
+
return this.dbStatus;
|
|
6894
|
+
}
|
|
6895
|
+
canMonitorDirectory(dir) {
|
|
6896
|
+
if (fs9.existsSync(dir))
|
|
6897
|
+
return true;
|
|
6898
|
+
const db = this.ensureDb();
|
|
6899
|
+
if (!db)
|
|
6900
|
+
return false;
|
|
6901
|
+
const projectId = extractProjectIdFromDbPath(dir + path7.sep + "dummy.json") || path7.basename(dir);
|
|
6902
|
+
return projectId.length > 0 && db.hasProject(projectId);
|
|
6903
|
+
}
|
|
6904
|
+
mapDbSessions(projectId) {
|
|
6905
|
+
const db = this.ensureDb();
|
|
6906
|
+
if (!db)
|
|
6907
|
+
return [];
|
|
6908
|
+
const sessions = db.getSessionsForProject(projectId);
|
|
6909
|
+
const dataDir = getOpenCodeDataDir();
|
|
6910
|
+
return sessions.map((session) => {
|
|
6911
|
+
const syntheticPath = makeDbSessionPath(dataDir, projectId, session.id);
|
|
6912
|
+
this.sessionMetaCache.set(syntheticPath, {
|
|
6913
|
+
title: session.title,
|
|
6914
|
+
timeUpdated: session.time_updated
|
|
6915
|
+
});
|
|
6916
|
+
return syntheticPath;
|
|
6917
|
+
});
|
|
6918
|
+
}
|
|
6791
6919
|
// --- Path resolution ---
|
|
6792
6920
|
getSessionDirectory(workspacePath) {
|
|
6793
6921
|
const db = this.ensureDb();
|
|
6794
|
-
const
|
|
6922
|
+
const dbStatus = this.getRuntimeStatus();
|
|
6923
|
+
const projectId = resolveProjectId(workspacePath, db, dbStatus);
|
|
6795
6924
|
if (projectId) {
|
|
6796
6925
|
if (db) {
|
|
6797
6926
|
return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
|
|
6798
6927
|
}
|
|
6799
6928
|
return path7.join(getStorageDir(), "session", projectId);
|
|
6800
6929
|
}
|
|
6930
|
+
if (dbStatus.kind !== "db_missing") {
|
|
6931
|
+
return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX);
|
|
6932
|
+
}
|
|
6801
6933
|
return path7.join(getStorageDir(), "session");
|
|
6802
6934
|
}
|
|
6803
6935
|
discoverSessionDirectory(workspacePath) {
|
|
6804
6936
|
const db = this.ensureDb();
|
|
6805
|
-
const
|
|
6937
|
+
const dbStatus = this.getRuntimeStatus();
|
|
6938
|
+
const projectId = resolveProjectId(workspacePath, db, dbStatus);
|
|
6806
6939
|
if (!projectId)
|
|
6807
6940
|
return null;
|
|
6808
6941
|
if (db) {
|
|
6809
|
-
|
|
6810
|
-
if (sessions.length > 0) {
|
|
6942
|
+
if (db.hasProject(projectId)) {
|
|
6811
6943
|
return path7.join(getOpenCodeDataDir(), DB_SESSION_PREFIX, projectId);
|
|
6812
6944
|
}
|
|
6813
6945
|
}
|
|
6946
|
+
if (dbStatus.kind !== "db_missing") {
|
|
6947
|
+
return null;
|
|
6948
|
+
}
|
|
6814
6949
|
const dir = path7.join(getStorageDir(), "session", projectId);
|
|
6815
6950
|
return fs9.existsSync(dir) ? dir : null;
|
|
6816
6951
|
}
|
|
6817
6952
|
// --- Session discovery ---
|
|
6818
6953
|
findActiveSession(workspacePath) {
|
|
6819
6954
|
const db = this.ensureDb();
|
|
6820
|
-
const
|
|
6955
|
+
const dbStatus = this.getRuntimeStatus();
|
|
6956
|
+
const projectId = resolveProjectId(workspacePath, db, dbStatus);
|
|
6821
6957
|
if (!projectId)
|
|
6822
6958
|
return null;
|
|
6823
6959
|
if (db) {
|
|
@@ -6831,6 +6967,9 @@ var require_openCode = __commonJS({
|
|
|
6831
6967
|
return syntheticPath;
|
|
6832
6968
|
}
|
|
6833
6969
|
}
|
|
6970
|
+
if (dbStatus.kind !== "db_missing") {
|
|
6971
|
+
return null;
|
|
6972
|
+
}
|
|
6834
6973
|
return this.findActiveSessionFromFiles(projectId);
|
|
6835
6974
|
}
|
|
6836
6975
|
findActiveSessionFromFiles(projectId) {
|
|
@@ -6883,58 +7022,37 @@ var require_openCode = __commonJS({
|
|
|
6883
7022
|
}
|
|
6884
7023
|
findAllSessions(workspacePath) {
|
|
6885
7024
|
const db = this.ensureDb();
|
|
6886
|
-
const
|
|
7025
|
+
const dbStatus = this.getRuntimeStatus();
|
|
7026
|
+
const projectId = resolveProjectId(workspacePath, db, dbStatus);
|
|
6887
7027
|
if (!projectId)
|
|
6888
7028
|
return [];
|
|
6889
7029
|
if (db) {
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
const syntheticPath = makeDbSessionPath(dataDir, projectId, s.id);
|
|
6895
|
-
this.sessionMetaCache.set(syntheticPath, {
|
|
6896
|
-
title: s.title,
|
|
6897
|
-
timeUpdated: s.time_updated
|
|
6898
|
-
});
|
|
6899
|
-
return syntheticPath;
|
|
6900
|
-
});
|
|
6901
|
-
}
|
|
7030
|
+
return this.mapDbSessions(projectId);
|
|
7031
|
+
}
|
|
7032
|
+
if (dbStatus.kind !== "db_missing") {
|
|
7033
|
+
return [];
|
|
6902
7034
|
}
|
|
6903
7035
|
const sessionDir = path7.join(getStorageDir(), "session", projectId);
|
|
6904
7036
|
return this.findSessionsInDirectoryFromFiles(sessionDir);
|
|
6905
7037
|
}
|
|
6906
7038
|
findSessionsInDirectory(dir) {
|
|
6907
7039
|
const db = this.ensureDb();
|
|
7040
|
+
const dbStatus = this.getRuntimeStatus();
|
|
6908
7041
|
if (db && dir.includes(path7.sep + DB_SESSION_PREFIX + path7.sep)) {
|
|
6909
7042
|
const projectId = extractProjectIdFromDbPath(dir + path7.sep + "dummy.json");
|
|
6910
7043
|
if (projectId) {
|
|
6911
|
-
|
|
6912
|
-
const dataDir = getOpenCodeDataDir();
|
|
6913
|
-
return sessions.map((s) => {
|
|
6914
|
-
const syntheticPath = makeDbSessionPath(dataDir, projectId, s.id);
|
|
6915
|
-
this.sessionMetaCache.set(syntheticPath, {
|
|
6916
|
-
title: s.title,
|
|
6917
|
-
timeUpdated: s.time_updated
|
|
6918
|
-
});
|
|
6919
|
-
return syntheticPath;
|
|
6920
|
-
});
|
|
7044
|
+
return this.mapDbSessions(projectId);
|
|
6921
7045
|
}
|
|
6922
7046
|
}
|
|
6923
7047
|
if (db) {
|
|
6924
7048
|
const dirName = path7.basename(dir);
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
const dataDir = getOpenCodeDataDir();
|
|
6928
|
-
return sessions.map((s) => {
|
|
6929
|
-
const syntheticPath = makeDbSessionPath(dataDir, dirName, s.id);
|
|
6930
|
-
this.sessionMetaCache.set(syntheticPath, {
|
|
6931
|
-
title: s.title,
|
|
6932
|
-
timeUpdated: s.time_updated
|
|
6933
|
-
});
|
|
6934
|
-
return syntheticPath;
|
|
6935
|
-
});
|
|
7049
|
+
if (db.hasProject(dirName)) {
|
|
7050
|
+
return this.mapDbSessions(dirName);
|
|
6936
7051
|
}
|
|
6937
7052
|
}
|
|
7053
|
+
if (dbStatus.kind !== "db_missing") {
|
|
7054
|
+
return [];
|
|
7055
|
+
}
|
|
6938
7056
|
return this.findSessionsInDirectoryFromFiles(dir);
|
|
6939
7057
|
}
|
|
6940
7058
|
findSessionsInDirectoryFromFiles(dir) {
|
|
@@ -6957,13 +7075,14 @@ var require_openCode = __commonJS({
|
|
|
6957
7075
|
getAllProjectFolders(workspacePath) {
|
|
6958
7076
|
const db = this.ensureDb();
|
|
6959
7077
|
const folders = [];
|
|
7078
|
+
const dbStatus = this.getRuntimeStatus();
|
|
6960
7079
|
if (db) {
|
|
6961
7080
|
const projects = db.getAllProjects();
|
|
6962
7081
|
const stats = db.getProjectSessionStats();
|
|
6963
7082
|
const statsMap = new Map(stats.map((s) => [s.projectId, s]));
|
|
6964
7083
|
let currentProjectId = null;
|
|
6965
7084
|
if (workspacePath) {
|
|
6966
|
-
currentProjectId = resolveProjectId(workspacePath, db);
|
|
7085
|
+
currentProjectId = resolveProjectId(workspacePath, db, dbStatus);
|
|
6967
7086
|
}
|
|
6968
7087
|
const dataDir = getOpenCodeDataDir();
|
|
6969
7088
|
for (const project of projects) {
|
|
@@ -6992,6 +7111,9 @@ var require_openCode = __commonJS({
|
|
|
6992
7111
|
if (folders.length > 0)
|
|
6993
7112
|
return folders;
|
|
6994
7113
|
}
|
|
7114
|
+
if (dbStatus.kind !== "db_missing") {
|
|
7115
|
+
return [];
|
|
7116
|
+
}
|
|
6995
7117
|
return this.getAllProjectFoldersFromFiles(workspacePath);
|
|
6996
7118
|
}
|
|
6997
7119
|
getAllProjectFoldersFromFiles(workspacePath) {
|
|
@@ -7077,7 +7199,7 @@ var require_openCode = __commonJS({
|
|
|
7077
7199
|
}
|
|
7078
7200
|
encodeWorkspacePath(workspacePath) {
|
|
7079
7201
|
const db = this.ensureDb();
|
|
7080
|
-
return resolveProjectId(workspacePath, db) || workspacePath;
|
|
7202
|
+
return resolveProjectId(workspacePath, db, this.getRuntimeStatus()) || workspacePath;
|
|
7081
7203
|
}
|
|
7082
7204
|
extractSessionLabel(sessionPath) {
|
|
7083
7205
|
const db = this.ensureDb();
|
|
@@ -19343,7 +19465,7 @@ var init_UpdateCheckService = __esm({
|
|
|
19343
19465
|
/** Run the update check (one-shot). */
|
|
19344
19466
|
async check() {
|
|
19345
19467
|
try {
|
|
19346
|
-
const current = "0.13.
|
|
19468
|
+
const current = "0.13.6";
|
|
19347
19469
|
const cached = this.readCache();
|
|
19348
19470
|
let latest;
|
|
19349
19471
|
if (cached && Date.now() - cached.checkedAt < CACHE_TTL_MS) {
|
|
@@ -58928,6 +59050,24 @@ var init_SessionPickerHelpers = __esm({
|
|
|
58928
59050
|
}
|
|
58929
59051
|
});
|
|
58930
59052
|
|
|
59053
|
+
// src/dashboard/branding.ts
|
|
59054
|
+
var BRAND_NAME, BRAND_TAGLINE, LOGO_ART, BRAND_INLINE;
|
|
59055
|
+
var init_branding = __esm({
|
|
59056
|
+
"src/dashboard/branding.ts"() {
|
|
59057
|
+
"use strict";
|
|
59058
|
+
BRAND_NAME = "SIDEKICK";
|
|
59059
|
+
BRAND_TAGLINE = "Agent Hub";
|
|
59060
|
+
LOGO_ART = [
|
|
59061
|
+
" {bold}{magenta-fg} ___ ___ ___ ___ _ _____ ___ _ __{/magenta-fg}{/bold}",
|
|
59062
|
+
" {bold}{magenta-fg}/ __|_ _| \\| __| |/ /_ _/ __| |/ /{/magenta-fg}{/bold}",
|
|
59063
|
+
" {bold}{magenta-fg}\\__ \\| || |) | _|| ' < | | (__| ' < {/magenta-fg}{/bold}",
|
|
59064
|
+
" {bold}{magenta-fg}|___/___|___/|___|_|\\_\\___\\___|_|\\_\\{/magenta-fg}{/bold}",
|
|
59065
|
+
` {grey-fg}${BRAND_TAGLINE}{/grey-fg}`
|
|
59066
|
+
];
|
|
59067
|
+
BRAND_INLINE = `{bold}{magenta-fg}${BRAND_NAME}{/magenta-fg}{/bold}`;
|
|
59068
|
+
}
|
|
59069
|
+
});
|
|
59070
|
+
|
|
58931
59071
|
// node_modules/react/cjs/react-jsx-runtime.production.js
|
|
58932
59072
|
var require_react_jsx_runtime_production = __commonJS({
|
|
58933
59073
|
"node_modules/react/cjs/react-jsx-runtime.production.js"(exports) {
|
|
@@ -59227,6 +59367,81 @@ var require_jsx_runtime = __commonJS({
|
|
|
59227
59367
|
}
|
|
59228
59368
|
});
|
|
59229
59369
|
|
|
59370
|
+
// src/dashboard/ink/parseBlessedTags.tsx
|
|
59371
|
+
function mapColor(c) {
|
|
59372
|
+
if (c === "grey") return "gray";
|
|
59373
|
+
return c;
|
|
59374
|
+
}
|
|
59375
|
+
function parseBlessedTags(input) {
|
|
59376
|
+
if (!input) return null;
|
|
59377
|
+
if (!input.includes("{")) {
|
|
59378
|
+
return input;
|
|
59379
|
+
}
|
|
59380
|
+
const segments = [];
|
|
59381
|
+
const styleStack = [{}];
|
|
59382
|
+
let lastIndex = 0;
|
|
59383
|
+
let segKey = 0;
|
|
59384
|
+
TAG_RE.lastIndex = 0;
|
|
59385
|
+
let match;
|
|
59386
|
+
while ((match = TAG_RE.exec(input)) !== null) {
|
|
59387
|
+
const beforeText = input.slice(lastIndex, match.index);
|
|
59388
|
+
if (beforeText) {
|
|
59389
|
+
segments.push(renderSpan(beforeText, currentStyle(styleStack), segKey++));
|
|
59390
|
+
}
|
|
59391
|
+
lastIndex = match.index + match[0].length;
|
|
59392
|
+
const isClose = match[1] === "/";
|
|
59393
|
+
const tagName = match[2];
|
|
59394
|
+
if (isClose) {
|
|
59395
|
+
if (styleStack.length > 1) {
|
|
59396
|
+
styleStack.pop();
|
|
59397
|
+
}
|
|
59398
|
+
} else if (tagName === "center") {
|
|
59399
|
+
} else if (tagName === "bold") {
|
|
59400
|
+
styleStack.push({ ...currentStyle(styleStack), bold: true });
|
|
59401
|
+
} else if (tagName === "underline") {
|
|
59402
|
+
styleStack.push({ ...currentStyle(styleStack), underline: true });
|
|
59403
|
+
} else if (tagName.endsWith("-fg")) {
|
|
59404
|
+
const color = tagName.slice(0, -3);
|
|
59405
|
+
styleStack.push({ ...currentStyle(styleStack), color: mapColor(color) });
|
|
59406
|
+
}
|
|
59407
|
+
}
|
|
59408
|
+
const trailing = input.slice(lastIndex);
|
|
59409
|
+
if (trailing) {
|
|
59410
|
+
segments.push(renderSpan(trailing, currentStyle(styleStack), segKey++));
|
|
59411
|
+
}
|
|
59412
|
+
if (segments.length === 0) return null;
|
|
59413
|
+
if (segments.length === 1) return segments[0];
|
|
59414
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: segments });
|
|
59415
|
+
}
|
|
59416
|
+
function currentStyle(stack) {
|
|
59417
|
+
return stack[stack.length - 1];
|
|
59418
|
+
}
|
|
59419
|
+
function renderSpan(text, style, key) {
|
|
59420
|
+
const hasStyle = style.bold || style.underline || style.color;
|
|
59421
|
+
if (!hasStyle) {
|
|
59422
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { children: text }, key);
|
|
59423
|
+
}
|
|
59424
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
59425
|
+
Text,
|
|
59426
|
+
{
|
|
59427
|
+
bold: style.bold,
|
|
59428
|
+
underline: style.underline,
|
|
59429
|
+
color: style.color,
|
|
59430
|
+
children: text
|
|
59431
|
+
},
|
|
59432
|
+
key
|
|
59433
|
+
);
|
|
59434
|
+
}
|
|
59435
|
+
var import_jsx_runtime, TAG_RE;
|
|
59436
|
+
var init_parseBlessedTags = __esm({
|
|
59437
|
+
async "src/dashboard/ink/parseBlessedTags.tsx"() {
|
|
59438
|
+
"use strict";
|
|
59439
|
+
await init_build2();
|
|
59440
|
+
import_jsx_runtime = __toESM(require_jsx_runtime(), 1);
|
|
59441
|
+
TAG_RE = /\{(\/?)([^}]+)\}/g;
|
|
59442
|
+
}
|
|
59443
|
+
});
|
|
59444
|
+
|
|
59230
59445
|
// src/dashboard/ink/SessionPickerInk.tsx
|
|
59231
59446
|
function buildGroupedRows(items) {
|
|
59232
59447
|
const providerIds = new Set(items.map((it) => it.providerId).filter(Boolean));
|
|
@@ -59278,8 +59493,8 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59278
59493
|
const viewportHeight = Math.max(5, rows - 15);
|
|
59279
59494
|
function renderSessionRow(i, isSelected) {
|
|
59280
59495
|
if (i === items.length) {
|
|
59281
|
-
return /* @__PURE__ */ (0,
|
|
59282
|
-
/* @__PURE__ */ (0,
|
|
59496
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { inverse: isSelected, children: [
|
|
59497
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "yellow", children: "+" }),
|
|
59283
59498
|
" Wait for a new session to start..."
|
|
59284
59499
|
] }) }, "wait");
|
|
59285
59500
|
}
|
|
@@ -59288,21 +59503,21 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59288
59503
|
const dotColor = item.isActive ? "green" : "gray";
|
|
59289
59504
|
const badge = item.providerId ? PROVIDER_BADGES[item.providerId] : null;
|
|
59290
59505
|
const truncLabel = item.label.length > 40 ? item.label.substring(0, 37) + "..." : item.label;
|
|
59291
|
-
return /* @__PURE__ */ (0,
|
|
59292
|
-
badge && /* @__PURE__ */ (0,
|
|
59506
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { inverse: isSelected, children: [
|
|
59507
|
+
badge && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: badge.color, children: [
|
|
59293
59508
|
"[",
|
|
59294
59509
|
badge.badge,
|
|
59295
59510
|
"]"
|
|
59296
59511
|
] }),
|
|
59297
59512
|
" ",
|
|
59298
|
-
/* @__PURE__ */ (0,
|
|
59513
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: dotColor, children: dot }),
|
|
59299
59514
|
" ",
|
|
59300
59515
|
truncLabel.padEnd(40),
|
|
59301
59516
|
" ",
|
|
59302
|
-
/* @__PURE__ */ (0,
|
|
59517
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: item.age.padEnd(9) }),
|
|
59303
59518
|
" ",
|
|
59304
|
-
/* @__PURE__ */ (0,
|
|
59305
|
-
item.isActive ? /* @__PURE__ */ (0,
|
|
59519
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: item.sessionId }),
|
|
59520
|
+
item.isActive ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "green", children: " LIVE" }) : ""
|
|
59306
59521
|
] }) }, item.sessionPath);
|
|
59307
59522
|
}
|
|
59308
59523
|
function renderList() {
|
|
@@ -59329,14 +59544,14 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59329
59544
|
const name = PROVIDER_NAMES[row.providerId] || row.providerId;
|
|
59330
59545
|
const badge = PROVIDER_BADGES[row.providerId];
|
|
59331
59546
|
elements.push(
|
|
59332
|
-
/* @__PURE__ */ (0,
|
|
59333
|
-
/* @__PURE__ */ (0,
|
|
59547
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { children: [
|
|
59548
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
|
|
59334
59549
|
"\u2500\u2500",
|
|
59335
59550
|
" "
|
|
59336
59551
|
] }),
|
|
59337
|
-
badge && /* @__PURE__ */ (0,
|
|
59338
|
-
!badge && /* @__PURE__ */ (0,
|
|
59339
|
-
/* @__PURE__ */ (0,
|
|
59552
|
+
badge && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: badge.color, children: name }),
|
|
59553
|
+
!badge && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: name }),
|
|
59554
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { dimColor: true, children: [
|
|
59340
59555
|
" ",
|
|
59341
59556
|
"\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
|
|
59342
59557
|
] })
|
|
@@ -59358,12 +59573,9 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59358
59573
|
return renderSessionRow(i, i === selectedIndex);
|
|
59359
59574
|
});
|
|
59360
59575
|
}
|
|
59361
|
-
return /* @__PURE__ */ (0,
|
|
59362
|
-
/* @__PURE__ */ (0,
|
|
59363
|
-
|
|
59364
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(Text, { bold: true, children: " Agent Hub" })
|
|
59365
|
-
] }) }),
|
|
59366
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
59576
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Box_default, { flexDirection: "column", width: "100%", height: rows, children: [
|
|
59577
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { justifyContent: "center", marginTop: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { flexDirection: "column", width: 50, children: LOGO_ART.map((line, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { children: parseBlessedTags(line) }, `logo-${i}`)) }) }),
|
|
59578
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
59367
59579
|
Box_default,
|
|
59368
59580
|
{
|
|
59369
59581
|
flexDirection: "column",
|
|
@@ -59372,7 +59584,7 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59372
59584
|
marginX: 2,
|
|
59373
59585
|
flexGrow: 1,
|
|
59374
59586
|
children: [
|
|
59375
|
-
/* @__PURE__ */ (0,
|
|
59587
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { color: "magenta", children: [
|
|
59376
59588
|
" Sessions (",
|
|
59377
59589
|
items.length,
|
|
59378
59590
|
") "
|
|
@@ -59381,21 +59593,21 @@ function SessionPickerInk({ items, onSelect }) {
|
|
|
59381
59593
|
]
|
|
59382
59594
|
}
|
|
59383
59595
|
),
|
|
59384
|
-
/* @__PURE__ */ (0,
|
|
59596
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Box_default, { height: 1, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { children: [
|
|
59385
59597
|
" ",
|
|
59386
|
-
|
|
59598
|
+
parseBlessedTags(BRAND_INLINE),
|
|
59387
59599
|
" ",
|
|
59388
|
-
/* @__PURE__ */ (0,
|
|
59600
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { color: "gray", children: "Session Picker" }),
|
|
59389
59601
|
" ",
|
|
59390
|
-
/* @__PURE__ */ (0,
|
|
59602
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(Text, { bold: true, children: [
|
|
59391
59603
|
"\u2191",
|
|
59392
59604
|
"/",
|
|
59393
59605
|
"\u2193"
|
|
59394
59606
|
] }),
|
|
59395
59607
|
" navigate ",
|
|
59396
|
-
/* @__PURE__ */ (0,
|
|
59608
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: true, children: "Enter" }),
|
|
59397
59609
|
" select ",
|
|
59398
|
-
/* @__PURE__ */ (0,
|
|
59610
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(Text, { bold: true, children: "q" }),
|
|
59399
59611
|
" quit"
|
|
59400
59612
|
] }) })
|
|
59401
59613
|
] });
|
|
@@ -59414,7 +59626,7 @@ async function showSessionPicker(provider, workspacePath, additionalProviders) {
|
|
|
59414
59626
|
if (items.length === 0) return { sessionPath: null };
|
|
59415
59627
|
return new Promise((resolve, reject) => {
|
|
59416
59628
|
const instance = render2(
|
|
59417
|
-
/* @__PURE__ */ (0,
|
|
59629
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
59418
59630
|
SessionPickerInk,
|
|
59419
59631
|
{
|
|
59420
59632
|
items,
|
|
@@ -59431,14 +59643,16 @@ async function showSessionPicker(provider, workspacePath, additionalProviders) {
|
|
|
59431
59643
|
}).catch(reject);
|
|
59432
59644
|
});
|
|
59433
59645
|
}
|
|
59434
|
-
var import_react29,
|
|
59646
|
+
var import_react29, import_jsx_runtime2, PROVIDER_NAMES;
|
|
59435
59647
|
var init_SessionPickerInk = __esm({
|
|
59436
59648
|
async "src/dashboard/ink/SessionPickerInk.tsx"() {
|
|
59437
59649
|
"use strict";
|
|
59438
59650
|
import_react29 = __toESM(require_react(), 1);
|
|
59439
59651
|
await init_build2();
|
|
59440
59652
|
init_SessionPickerHelpers();
|
|
59441
|
-
|
|
59653
|
+
init_branding();
|
|
59654
|
+
await init_parseBlessedTags();
|
|
59655
|
+
import_jsx_runtime2 = __toESM(require_jsx_runtime(), 1);
|
|
59442
59656
|
PROVIDER_NAMES = {
|
|
59443
59657
|
"claude-code": "Claude Code",
|
|
59444
59658
|
"opencode": "OpenCode",
|
|
@@ -59587,19 +59801,19 @@ var init_useWindowedScroll = __esm({
|
|
|
59587
59801
|
// src/dashboard/ink/TabBar.tsx
|
|
59588
59802
|
function TabBar({ panels, activeIndex, layoutMode }) {
|
|
59589
59803
|
const modeLabel = MODE_LABELS[layoutMode] || layoutMode;
|
|
59590
|
-
return /* @__PURE__ */ (0,
|
|
59591
|
-
/* @__PURE__ */ (0,
|
|
59804
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Box_default, { height: 1, width: "100%", children: [
|
|
59805
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { flexGrow: 1, children: panels.map((p, i) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { marginRight: 1, children: i === activeIndex ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { bold: true, underline: true, color: "magenta", children: [
|
|
59592
59806
|
p.shortcutKey,
|
|
59593
59807
|
" ",
|
|
59594
59808
|
p.title
|
|
59595
|
-
] }) : /* @__PURE__ */ (0,
|
|
59596
|
-
/* @__PURE__ */ (0,
|
|
59597
|
-
/* @__PURE__ */ (0,
|
|
59809
|
+
] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
59810
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { dimColor: true, children: p.shortcutKey }),
|
|
59811
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { color: "gray", children: [
|
|
59598
59812
|
" ",
|
|
59599
59813
|
p.title
|
|
59600
59814
|
] })
|
|
59601
59815
|
] }) }, p.id)) }),
|
|
59602
|
-
/* @__PURE__ */ (0,
|
|
59816
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Box_default, { children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(Text, { dimColor: true, children: [
|
|
59603
59817
|
"z: ",
|
|
59604
59818
|
modeLabel,
|
|
59605
59819
|
" ",
|
|
@@ -59607,12 +59821,12 @@ function TabBar({ panels, activeIndex, layoutMode }) {
|
|
|
59607
59821
|
] }) })
|
|
59608
59822
|
] });
|
|
59609
59823
|
}
|
|
59610
|
-
var
|
|
59824
|
+
var import_jsx_runtime3, MODE_LABELS;
|
|
59611
59825
|
var init_TabBar = __esm({
|
|
59612
59826
|
async "src/dashboard/ink/TabBar.tsx"() {
|
|
59613
59827
|
"use strict";
|
|
59614
59828
|
await init_build2();
|
|
59615
|
-
|
|
59829
|
+
import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
59616
59830
|
MODE_LABELS = {
|
|
59617
59831
|
normal: "Normal",
|
|
59618
59832
|
expanded: "Expanded",
|
|
@@ -59621,81 +59835,6 @@ var init_TabBar = __esm({
|
|
|
59621
59835
|
}
|
|
59622
59836
|
});
|
|
59623
59837
|
|
|
59624
|
-
// src/dashboard/ink/parseBlessedTags.tsx
|
|
59625
|
-
function mapColor(c) {
|
|
59626
|
-
if (c === "grey") return "gray";
|
|
59627
|
-
return c;
|
|
59628
|
-
}
|
|
59629
|
-
function parseBlessedTags(input) {
|
|
59630
|
-
if (!input) return null;
|
|
59631
|
-
if (!input.includes("{")) {
|
|
59632
|
-
return input;
|
|
59633
|
-
}
|
|
59634
|
-
const segments = [];
|
|
59635
|
-
const styleStack = [{}];
|
|
59636
|
-
let lastIndex = 0;
|
|
59637
|
-
let segKey = 0;
|
|
59638
|
-
TAG_RE.lastIndex = 0;
|
|
59639
|
-
let match;
|
|
59640
|
-
while ((match = TAG_RE.exec(input)) !== null) {
|
|
59641
|
-
const beforeText = input.slice(lastIndex, match.index);
|
|
59642
|
-
if (beforeText) {
|
|
59643
|
-
segments.push(renderSpan(beforeText, currentStyle(styleStack), segKey++));
|
|
59644
|
-
}
|
|
59645
|
-
lastIndex = match.index + match[0].length;
|
|
59646
|
-
const isClose = match[1] === "/";
|
|
59647
|
-
const tagName = match[2];
|
|
59648
|
-
if (isClose) {
|
|
59649
|
-
if (styleStack.length > 1) {
|
|
59650
|
-
styleStack.pop();
|
|
59651
|
-
}
|
|
59652
|
-
} else if (tagName === "center") {
|
|
59653
|
-
} else if (tagName === "bold") {
|
|
59654
|
-
styleStack.push({ ...currentStyle(styleStack), bold: true });
|
|
59655
|
-
} else if (tagName === "underline") {
|
|
59656
|
-
styleStack.push({ ...currentStyle(styleStack), underline: true });
|
|
59657
|
-
} else if (tagName.endsWith("-fg")) {
|
|
59658
|
-
const color = tagName.slice(0, -3);
|
|
59659
|
-
styleStack.push({ ...currentStyle(styleStack), color: mapColor(color) });
|
|
59660
|
-
}
|
|
59661
|
-
}
|
|
59662
|
-
const trailing = input.slice(lastIndex);
|
|
59663
|
-
if (trailing) {
|
|
59664
|
-
segments.push(renderSpan(trailing, currentStyle(styleStack), segKey++));
|
|
59665
|
-
}
|
|
59666
|
-
if (segments.length === 0) return null;
|
|
59667
|
-
if (segments.length === 1) return segments[0];
|
|
59668
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: segments });
|
|
59669
|
-
}
|
|
59670
|
-
function currentStyle(stack) {
|
|
59671
|
-
return stack[stack.length - 1];
|
|
59672
|
-
}
|
|
59673
|
-
function renderSpan(text, style, key) {
|
|
59674
|
-
const hasStyle = style.bold || style.underline || style.color;
|
|
59675
|
-
if (!hasStyle) {
|
|
59676
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(Text, { children: text }, key);
|
|
59677
|
-
}
|
|
59678
|
-
return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
59679
|
-
Text,
|
|
59680
|
-
{
|
|
59681
|
-
bold: style.bold,
|
|
59682
|
-
underline: style.underline,
|
|
59683
|
-
color: style.color,
|
|
59684
|
-
children: text
|
|
59685
|
-
},
|
|
59686
|
-
key
|
|
59687
|
-
);
|
|
59688
|
-
}
|
|
59689
|
-
var import_jsx_runtime3, TAG_RE;
|
|
59690
|
-
var init_parseBlessedTags = __esm({
|
|
59691
|
-
async "src/dashboard/ink/parseBlessedTags.tsx"() {
|
|
59692
|
-
"use strict";
|
|
59693
|
-
await init_build2();
|
|
59694
|
-
import_jsx_runtime3 = __toESM(require_jsx_runtime(), 1);
|
|
59695
|
-
TAG_RE = /\{(\/?)([^}]+)\}/g;
|
|
59696
|
-
}
|
|
59697
|
-
});
|
|
59698
|
-
|
|
59699
59838
|
// src/dashboard/ink/SideList.tsx
|
|
59700
59839
|
function truncateTaggedLabel(label, maxVisible) {
|
|
59701
59840
|
const stripped = label.replace(TAG_RE2, "");
|
|
@@ -59912,13 +60051,10 @@ function StatusBar({
|
|
|
59912
60051
|
const statusColor = providerStatus?.indicator === "minor" ? "yellow" : "red";
|
|
59913
60052
|
return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { height: 1, width: "100%", children: [
|
|
59914
60053
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Box_default, { children: [
|
|
59915
|
-
/* @__PURE__ */ (0, import_jsx_runtime7.
|
|
59916
|
-
"\u26A1",
|
|
59917
|
-
" SIDEKICK"
|
|
59918
|
-
] }),
|
|
60054
|
+
/* @__PURE__ */ (0, import_jsx_runtime7.jsx)(Text, { children: parseBlessedTags(BRAND_INLINE) }),
|
|
59919
60055
|
/* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { dimColor: true, children: [
|
|
59920
60056
|
" v",
|
|
59921
|
-
"0.13.
|
|
60057
|
+
"0.13.6"
|
|
59922
60058
|
] }),
|
|
59923
60059
|
updateInfo && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(Text, { color: "yellow", children: [
|
|
59924
60060
|
" (v",
|
|
@@ -60013,6 +60149,8 @@ var init_StatusBar = __esm({
|
|
|
60013
60149
|
async "src/dashboard/ink/StatusBar.tsx"() {
|
|
60014
60150
|
"use strict";
|
|
60015
60151
|
await init_build2();
|
|
60152
|
+
init_branding();
|
|
60153
|
+
await init_parseBlessedTags();
|
|
60016
60154
|
import_jsx_runtime7 = __toESM(require_jsx_runtime(), 1);
|
|
60017
60155
|
}
|
|
60018
60156
|
});
|
|
@@ -60038,18 +60176,6 @@ var init_useSpinner = __esm({
|
|
|
60038
60176
|
}
|
|
60039
60177
|
});
|
|
60040
60178
|
|
|
60041
|
-
// src/dashboard/branding.ts
|
|
60042
|
-
var LOGO_ART;
|
|
60043
|
-
var init_branding = __esm({
|
|
60044
|
-
"src/dashboard/branding.ts"() {
|
|
60045
|
-
"use strict";
|
|
60046
|
-
LOGO_ART = [
|
|
60047
|
-
" {bold}{magenta-fg}\u26A1 S I D E K I C K{/magenta-fg}{/bold}",
|
|
60048
|
-
" {grey-fg}Agent Hub \xB7 Terminal Dashboard{/grey-fg}"
|
|
60049
|
-
];
|
|
60050
|
-
}
|
|
60051
|
-
});
|
|
60052
|
-
|
|
60053
60179
|
// src/dashboard/ink/SplashOverlay.tsx
|
|
60054
60180
|
function SplashOverlay() {
|
|
60055
60181
|
const spinner = useSpinner();
|
|
@@ -60306,7 +60432,7 @@ function ChangelogOverlay({ entries, scrollOffset }) {
|
|
|
60306
60432
|
" ",
|
|
60307
60433
|
/* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { bold: true, color: "cyan", children: [
|
|
60308
60434
|
"Terminal Dashboard v",
|
|
60309
|
-
"0.13.
|
|
60435
|
+
"0.13.6"
|
|
60310
60436
|
] }),
|
|
60311
60437
|
latestDate ? /* @__PURE__ */ (0, import_jsx_runtime12.jsxs)(Text, { color: "gray", children: [
|
|
60312
60438
|
" \u2014 ",
|
|
@@ -60628,7 +60754,7 @@ var init_mouse = __esm({
|
|
|
60628
60754
|
var CHANGELOG_default;
|
|
60629
60755
|
var init_CHANGELOG = __esm({
|
|
60630
60756
|
"CHANGELOG.md"() {
|
|
60631
|
-
CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.1] - 2026-03-04\n\n### Added\n\n- **`sidekick quota` Command**: One-shot subscription quota check showing 5-hour and 7-day utilization with color-coded progress bars and reset countdowns \u2014 supports `--json` for machine-readable output\n- **Quota Projections**: Elapsed-time projections shown in `sidekick quota` output and TUI dashboard quota section \u2014 displays projected end-of-window utilization next to current value (e.g., `40% \u2192 100%`), included in `--json` output as `projectedFiveHour` / `projectedSevenDay`\n\n## [0.13.0] - 2026-03-03\n\n_No CLI-specific changes in this release._\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
|
|
60757
|
+
CHANGELOG_default = '# Changelog\n\nAll notable changes to the Sidekick Agent Hub CLI will be documented in this file.\n\nThe format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),\nand this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).\n\n## [0.13.6] - 2026-03-11\n\n### Changed\n\n- **Refreshed CLI Dashboard Wordmark**: Updated the dashboard wordmark/header styling for a cleaner splash and dashboard identity\n\n### Fixed\n\n- **OpenCode dashboard startup**: OpenCode DB-backed session discovery now resolves projects by worktree, sandboxes, and session directory instead of quietly behaving like no session exists\n- **OpenCode runtime notices**: The CLI now prints an OpenCode-only actionable notice when `opencode.db` exists but `sqlite3` is missing, blocked, or otherwise unusable in the current shell environment\n\n## [0.13.5] - 2026-03-10\n\n### Added\n\n- **`sidekick status` Command**: One-shot Claude API status check with color-coded text output and `--json` mode\n- **Dashboard Status Banner**: Status bar shows a colored `\u25CF API minor/major/critical` indicator when Claude is degraded; Sessions panel Summary tab shows an "API Status" section with affected components and active incident details. Polls every 60s\n\n## [0.13.4] - 2026-03-08\n\n### Fixed\n\n- **Onboarding Phrase Spam**: Splash screen and detail pane motivational phrases memoized \u2014 no longer flicker every render tick (fixes [#13](https://github.com/cesarandreslopez/sidekick-agent-hub/issues/13))\n\n### Changed\n\n- **Simplified Logo**: Replaced 6-line ASCII robot art with compact text header in splash, help, and changelog overlays\n- **Removed Dead Code**: Removed unused `getSplashContent()` and `HELP_HEADER` exports from branding module\n\n## [0.13.3] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.2] - 2026-03-04\n\n_No CLI-specific changes in this release._\n\n## [0.13.1] - 2026-03-04\n\n### Added\n\n- **`sidekick quota` Command**: One-shot subscription quota check showing 5-hour and 7-day utilization with color-coded progress bars and reset countdowns \u2014 supports `--json` for machine-readable output\n- **Quota Projections**: Elapsed-time projections shown in `sidekick quota` output and TUI dashboard quota section \u2014 displays projected end-of-window utilization next to current value (e.g., `40% \u2192 100%`), included in `--json` output as `projectedFiveHour` / `projectedSevenDay`\n\n## [0.13.0] - 2026-03-03\n\n_No CLI-specific changes in this release._\n\n## [0.12.10] - 2026-03-01\n\n### Added\n\n- **Events Panel** (key 7): Scrollable live event stream with colored type badges (`[USR]`, `[AST]`, `[TOOL]`, `[RES]`), timestamps, and keyword-highlighted summaries; detail tabs for full event JSON and surrounding context\n- **Charts Panel** (key 8): Tool frequency horizontal bars, event type distribution, 60-minute activity heatmap using `\u2591\u2592\u2593\u2588` intensity characters, and pattern analysis with frequency bars and template text\n- **Multi-Mode Filter**: `/` filter overlay now supports four modes \u2014 substring, fuzzy, regex, and date range \u2014 Tab cycles modes, regex mode shows red validation errors\n- **Search Term Highlighting**: Active filter terms highlighted in blue within side list items\n- **Timeline Keyword Coloring**: Event summaries in the Sessions panel Timeline tab now use semantic keyword coloring \u2014 errors red, success green, tool names cyan, file paths magenta\n\n### Removed\n\n- **Search Panel**: Removed redundant Search panel (previously key 7) \u2014 the `/` filter with multi-mode support serves the same purpose\n\n## [0.12.9] - 2026-02-28\n\n### Added\n\n- **Standalone Data Commands**: `sidekick tasks`, `sidekick decisions`, `sidekick notes`, `sidekick stats`, `sidekick handoff` for accessing project data without launching the TUI\n- **`sidekick search <query>`**: Cross-session full-text search from the terminal\n- **`sidekick context`**: Composite output of tasks, decisions, notes, and handoff for piping into other tools\n- **`--list` flag on `sidekick dump`**: Discover available session IDs before requiring `--session <id>`\n- **Search Panel**: Search panel (panel 7) wired into the TUI dashboard\n\n### Changed\n\n- **`taskMerger` utility**: Duplicate `mergeTasks` logic extracted into shared `taskMerger` utility\n- **Model constants**: Hardcoded model IDs extracted to named constants\n\n### Fixed\n\n- **`convention` icon**: Notes panel icon replaced with valid `tip` type\n- **Linux clipboard**: Now supports Wayland (`wl-copy`) and `xsel` fallbacks, with error messages instead of silent failure\n- **`provider.dispose()`**: Added to `dump` and `report` commands (prevents SQLite connection leaks)\n\n## [0.12.8] - 2026-02-28\n\n### Changed\n\n- **Dashboard UI/UX Polish**: Visual overhaul for better hierarchy, consistency, and readability\n - Splash screen and help overlay now display the robot ASCII logo\n - Toast notifications show severity icons (\u2718 error, \u26A0 warning, \u25CF info) with inner padding\n - Focused pane uses double-border for clear focus indication\n - Section dividers (`\u2500\u2500 Title \u2500\u2500\u2500\u2500`) replace bare bold headers in summary, agents, and context attribution\n - Tab bar: active tab underlined in magenta, inactive tabs dimmed, bracket syntax removed\n - Status bar: segmented layout with `\u2502` separators; keys bold, labels dim\n - Summary metrics condensed: elapsed/events/compactions on one line, tokens on one line with cache rate and cost\n - Sparklines display peak metadata annotations\n - Progress bars use blessed color tags for consistent coloring\n - Help overlay uses dot-leader alignment for all keybinding rows\n - Empty state hints per panel (e.g. "Tasks appear as your agent works.")\n - Session picker groups sessions by provider with section headers when multiple providers are present\n\n## [0.12.7] - 2026-02-27\n\n### Added\n\n- **HTML Session Report**: `sidekick report` command generates a self-contained HTML report and opens it in the default browser\n - Options: `--session`, `--output`, `--theme` (dark/light), `--no-open`, `--no-thinking`\n - TUI Dashboard: press `r` to generate and open an HTML report for the current session\n\n## [0.12.6] - 2026-02-26\n\n### Added\n\n- **Session Dump Command**: `sidekick dump` exports session data in text, markdown, or JSON format with `--format`, `--width`, and `--expand` options\n- **Plans Panel Re-enabled**: Plans panel restored in CLI dashboard with plan file discovery from `~/.claude/plans/`\n- **Enhanced Status Bar**: Session info display improved with richer metadata\n\n### Fixed\n\n- **Old snapshot format migration**: Restoring pre-0.12.3 session snapshots no longer shows empty timeline entries\n\n### Changed\n\n- **Phrase library moved to shared**: CLI-specific phrase formatting kept local, all phrase content now from `sidekick-shared`\n\n## [0.12.5] - 2026-02-24\n\n### Fixed\n\n- **Update check too slow to notice new versions**: Reduced npm registry cache TTL from 24 hours to 4 hours so upgrade notices appear sooner after a new release\n\n## [0.12.4] - 2026-02-24\n\n### Fixed\n\n- **Session crash on upgrade**: Fixed `d.timestamp.getTime is not a function` error when restoring tool call data from session snapshots \u2014 `Date` objects were serialized to strings by JSON but not rehydrated on restore, causing the session monitor to crash on first run after upgrading from 0.12.2 to 0.12.3\n\n## [0.12.3] - 2026-02-24\n\n### Added\n\n- **Latest-node indicator**: The most recently added node in tree and boxed mind map views is now marked with a yellow indicator\n- **Plan analytics in mind map**: Tree and boxed views now display plan progress and per-step metrics\n - Tree view: plan header shows completion stats; steps show complexity, duration, tokens, tool calls, and errors in metadata brackets\n - Box view: progress bar with completion percentage; steps show right-aligned metrics; subtitle shows step count and total duration\n- **Cross-provider plan extraction**: Shared `PlanExtractor` now handles Claude Code (EnterPlanMode/ExitPlanMode) and OpenCode (`<proposed_plan>` XML) plans \u2014 previously only Codex plans were shown\n- **Enriched plan data model**: Plan steps include duration, token count, tool call count, and error messages\n- **Phase-grouped plan display**: When a plan has phase structure, tree and boxed views group steps under phase headers with context lines from the original plan markdown\n- **Node type filter**: Press `f` on the Mind Map tab to cycle through node type filters (file, tool, task, subagent, command, plan, knowledge-note) \u2014 non-matching sections render dimmed in grey\n\n### Fixed\n\n- **Kanban board regression**: Subagent and plan-step tasks now correctly appear in the kanban board\n\n### Changed\n\n- **Plans panel temporarily disabled**: The Plans panel in the CLI dashboard is disabled until plan-mode event capture is reliably working end-to-end. Plan nodes in the mind map remain active.\n- `DashboardState` now delegates to shared `EventAggregator` instead of maintaining its own aggregation logic\n\n## [0.12.2] - 2026-02-23\n\n### Added\n\n- **Update notifications**: The dashboard now checks the npm registry for newer versions on startup and shows a yellow banner in the status bar when an update is available (e.g., `v0.13.0 available \u2014 npm i -g sidekick-agent-hub`). Results are cached for 24 hours to avoid repeated network requests.\n\n## [0.12.1] - 2026-02-23\n\n### Fixed\n\n- **VS Code integration**: Fixed exit code 127 when the extension launches the CLI dashboard on systems using nvm or volta (node binary not found when shell init is bypassed)\n\n## [0.12.0] - 2026-02-22\n\n### Added\n\n- **"Open CLI Dashboard" VS Code Integration**: New VS Code command `Sidekick: Open CLI Dashboard` launches the TUI dashboard in an integrated terminal\n - Install the CLI with `npm install -g sidekick-agent-hub`\n\n## [0.11.0] - 2026-02-19\n\n### Added\n\n- **Initial Release**: Full-screen TUI dashboard for monitoring agent sessions from the terminal\n - Ink-based terminal UI with panels for sessions, tasks, kanban, mind map, notes, decisions, search, files, and git diff\n - Multi-provider support: auto-detects Claude Code, OpenCode, and Codex sessions\n - Reads from `~/.config/sidekick/` \u2014 the same data files the VS Code extension writes\n - Usage: `sidekick dashboard [--project <path>] [--provider <id>]`\n';
|
|
60632
60758
|
}
|
|
60633
60759
|
});
|
|
60634
60760
|
|
|
@@ -61412,7 +61538,8 @@ var init_Dashboard = __esm({
|
|
|
61412
61538
|
// src/commands/dashboard.ts
|
|
61413
61539
|
var dashboard_exports = {};
|
|
61414
61540
|
__export(dashboard_exports, {
|
|
61415
|
-
dashboardAction: () => dashboardAction
|
|
61541
|
+
dashboardAction: () => dashboardAction,
|
|
61542
|
+
getProviderRuntimeIssue: () => getProviderRuntimeIssue
|
|
61416
61543
|
});
|
|
61417
61544
|
import * as path4 from "path";
|
|
61418
61545
|
import * as os3 from "os";
|
|
@@ -61428,6 +61555,18 @@ function createProviderById(id) {
|
|
|
61428
61555
|
return new import_sidekick_shared14.ClaudeCodeProvider();
|
|
61429
61556
|
}
|
|
61430
61557
|
}
|
|
61558
|
+
function getProviderRuntimeIssue(provider) {
|
|
61559
|
+
if (provider.id !== "opencode") {
|
|
61560
|
+
return null;
|
|
61561
|
+
}
|
|
61562
|
+
const status = provider.getRuntimeStatus?.();
|
|
61563
|
+
if (!status || status.available || status.kind === "db_missing") {
|
|
61564
|
+
return null;
|
|
61565
|
+
}
|
|
61566
|
+
const detail = status.message ? ` ${status.message}` : "";
|
|
61567
|
+
const recommendation = status.kind === "sqlite_missing" ? " Recommendation: install `sqlite3`, ensure it is on PATH for the current shell, then retry." : status.kind === "sqlite_blocked" ? " Recommendation: ensure `sqlite3` is executable in the same environment as this shell, then retry." : " Recommendation: verify `sqlite3` can read `opencode.db` in the current environment, then retry.";
|
|
61568
|
+
return `${provider.displayName} session database is unavailable.${detail}${recommendation}`;
|
|
61569
|
+
}
|
|
61431
61570
|
async function dashboardAction(_opts, cmd) {
|
|
61432
61571
|
const globalOpts = cmd.parent.opts();
|
|
61433
61572
|
const opts = cmd.opts();
|
|
@@ -61436,14 +61575,18 @@ async function dashboardAction(_opts, cmd) {
|
|
|
61436
61575
|
let sessionId = opts.session;
|
|
61437
61576
|
let replay = !!opts.replay;
|
|
61438
61577
|
let activeProvider = provider;
|
|
61578
|
+
const providerIssue = getProviderRuntimeIssue(provider);
|
|
61439
61579
|
const detectedIds = (0, import_sidekick_shared13.getAllDetectedProviders)();
|
|
61440
61580
|
const additionalProviders = detectedIds.filter((id) => id !== provider.id).map((id) => createProviderById(id));
|
|
61441
61581
|
if (!sessionId) {
|
|
61442
|
-
const sessions = provider.findAllSessions(workspacePath);
|
|
61443
|
-
const
|
|
61582
|
+
const sessions = providerIssue ? [] : provider.findAllSessions(workspacePath);
|
|
61583
|
+
const healthyAdditionalProviders = additionalProviders.filter((p) => !getProviderRuntimeIssue(p));
|
|
61584
|
+
const hasAnySessions = sessions.length > 0 || healthyAdditionalProviders.some((p) => p.findAllSessions(workspacePath).length > 0);
|
|
61444
61585
|
if (hasAnySessions) {
|
|
61445
61586
|
try {
|
|
61446
|
-
const
|
|
61587
|
+
const pickerProvider = providerIssue ? healthyAdditionalProviders[0] || provider : provider;
|
|
61588
|
+
const pickerAdditionalProviders = providerIssue ? healthyAdditionalProviders.slice(1) : healthyAdditionalProviders;
|
|
61589
|
+
const result = await showSessionPicker(pickerProvider, workspacePath, pickerAdditionalProviders);
|
|
61447
61590
|
if (result.sessionPath) {
|
|
61448
61591
|
sessionId = path4.basename(result.sessionPath, path4.extname(result.sessionPath));
|
|
61449
61592
|
replay = true;
|
|
@@ -61455,11 +61598,22 @@ async function dashboardAction(_opts, cmd) {
|
|
|
61455
61598
|
for (const p of additionalProviders) p.dispose();
|
|
61456
61599
|
process.exit(0);
|
|
61457
61600
|
}
|
|
61601
|
+
} else if (providerIssue) {
|
|
61602
|
+
for (const p of additionalProviders) p.dispose();
|
|
61603
|
+
console.error(providerIssue);
|
|
61604
|
+
process.exitCode = 1;
|
|
61605
|
+
return;
|
|
61458
61606
|
}
|
|
61459
61607
|
}
|
|
61460
61608
|
for (const p of additionalProviders) {
|
|
61461
61609
|
if (p !== activeProvider) p.dispose();
|
|
61462
61610
|
}
|
|
61611
|
+
const activeProviderIssue = getProviderRuntimeIssue(activeProvider);
|
|
61612
|
+
if (activeProviderIssue) {
|
|
61613
|
+
console.error(activeProviderIssue);
|
|
61614
|
+
process.exitCode = 1;
|
|
61615
|
+
return;
|
|
61616
|
+
}
|
|
61463
61617
|
let staticData;
|
|
61464
61618
|
try {
|
|
61465
61619
|
staticData = await loadStaticData(workspacePath);
|
|
@@ -62850,7 +63004,7 @@ var init_cli = __esm({
|
|
|
62850
63004
|
import_sidekick_shared25 = __toESM(require_dist(), 1);
|
|
62851
63005
|
import_sidekick_shared26 = __toESM(require_dist(), 1);
|
|
62852
63006
|
program2 = new Command();
|
|
62853
|
-
program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.13.
|
|
63007
|
+
program2.name("sidekick").description("Query Sidekick project intelligence from the command line").version("0.13.6").option("--json", "Output as JSON").option("--project <path>", "Override project path (default: cwd)").option("--provider <id>", "Provider: claude-code, opencode, codex, auto (default: auto)");
|
|
62854
63008
|
dashCmd = new Command("dashboard").description("Full-screen TUI dashboard with live session metrics").option("--session <id>", "Follow a specific session (default: most recent)").option("--replay", "Replay existing events before streaming new ones").action(async (_opts, cmd) => {
|
|
62855
63009
|
const { dashboardAction: dashboardAction2 } = await init_dashboard().then(() => dashboard_exports);
|
|
62856
63010
|
return dashboardAction2(_opts, cmd);
|