Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions main.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
const { app, BrowserWindow, dialog, ipcMain, Menu, screen, shell } = require('electron');
const { Worker } = require('worker_threads');

Check warning on line 2 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'Worker' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 2 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'Worker' is assigned a value but never used. Allowed unused vars must match /^_/u
const { execFile } = require('child_process');
const path = require('path');
const fs = require('fs');
Expand All @@ -16,7 +16,7 @@
}

// getFolderIndexMtimeMs moved to session-cache.js
const { startMcpServer, shutdownMcpServer, shutdownAll: shutdownAllMcp, resolvePendingDiff, rekeyMcpServer, cleanStaleLockFiles } = require('./mcp-bridge');

Check warning on line 19 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'cleanStaleLockFiles' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 19 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'cleanStaleLockFiles' is assigned a value but never used. Allowed unused vars must match /^_/u
const { fetchAndTransformUsage } = require('./claude-auth');
log.transports.file.level = app.isPackaged ? 'info' : 'debug';
log.transports.console.level = app.isPackaged ? 'info' : 'debug';
Expand All @@ -36,7 +36,7 @@
);

// Shell profiles → shell-profiles.js
const { discoverShellProfiles, getShellProfiles, resolveShell, isWindows, isWslShell, windowsToWslPath, shellArgs } = require('./shell-profiles');

Check warning on line 39 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'isWindows' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 39 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'discoverShellProfiles' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 39 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'isWindows' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 39 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'discoverShellProfiles' is assigned a value but never used. Allowed unused vars must match /^_/u
const { startScheduler } = require('./schedule-runner');
const { encodeProjectPath } = require('./encode-project-path');

Expand Down Expand Up @@ -73,7 +73,7 @@
getMeta, getAllMeta, toggleStar, setName, setArchived,
isCachePopulated, getAllCached, getCachedByFolder, getCachedByParent, getCachedFolder, getCachedSession, upsertCachedSessions,
deleteCachedSession, deleteCachedFolder,
getFolderMeta, getAllFolderMeta, setFolderMeta,

Check warning on line 76 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'getFolderMeta' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 76 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'getFolderMeta' is assigned a value but never used. Allowed unused vars must match /^_/u
upsertSearchEntries, updateSearchTitle, deleteSearchSession, deleteSearchFolder, deleteSearchType,
searchByType, isSearchIndexPopulated, searchFtsRecreated,
getSetting, setSetting, deleteSetting,
Expand Down Expand Up @@ -289,8 +289,9 @@
setFolderMeta, getAllFolderMeta, getAllMeta, getAllCached, getSetting, getMeta, setName,
},
});
const { readSessionFile, readFolderFromFilesystem, refreshFolder, populateCacheFromFilesystem,

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'populateCacheFromFilesystem' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'readFolderFromFilesystem' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'readSessionFile' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'populateCacheFromFilesystem' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'readFolderFromFilesystem' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 292 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'readSessionFile' is assigned a value but never used. Allowed unused vars must match /^_/u
buildProjectsFromCache, notifyRendererProjectsChanged, sendStatus, populateCacheViaWorker } = sessionCache;

Check warning on line 293 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'sendStatus' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 293 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'sendStatus' is assigned a value but never used. Allowed unused vars must match /^_/u
const { resolveJsonlPath } = require('./read-session-file');


// --- IPC: browse-folder ---
Expand Down Expand Up @@ -444,6 +445,26 @@
});
});

// --- IPC: worktree-status ---
ipcMain.handle('worktree-status', (_event, worktreePath) => {
return new Promise((resolve) => {
const normalizedPath = worktreePath.replace(/\/$/, '');
const match = normalizedPath.match(WORKTREE_PATH_RE);
if (!match) {
return resolve({ ok: false, error: 'Path does not match a recognized worktree layout' });
}
const parentRepo = match[1];

execFile('git', ['-C', parentRepo, '-C', normalizedPath, 'status', '--porcelain'], (err, stdout, stderr) => {
if (err) {
return resolve({ ok: false, error: (stderr || err.message || String(err)).trim() });
}
const dirty = stdout.split('\n').map(l => l.trimEnd()).filter(Boolean);
resolve({ ok: true, dirty, total: dirty.length });
});
});
});

// --- IPC: get-projects ---
ipcMain.handle('open-external', (_event, url) => {
log.info('[open-external IPC]', url);
Expand Down Expand Up @@ -997,7 +1018,7 @@
ipcMain.handle('read-subagent-jsonl', (_event, parentSessionId, agentId) => {
const row = getCachedSession('sub:' + parentSessionId + ':' + agentId);
if (!row) return { error: 'Subagent session not found in cache' };
const jsonlPath = path.join(PROJECTS_DIR, row.folder, parentSessionId, 'subagents', 'agent-' + agentId + '.jsonl');
const jsonlPath = resolveJsonlPath(PROJECTS_DIR, row);
try {
const content = fs.readFileSync(jsonlPath, 'utf-8');
const entries = [];
Expand Down Expand Up @@ -1027,7 +1048,7 @@
ipcMain.handle('start-subagent-watch', (_event, parentSessionId, agentId) => {
const row = getCachedSession('sub:' + parentSessionId + ':' + agentId);
if (!row) return { error: 'Subagent not found in cache' };
const filePath = path.join(PROJECTS_DIR, row.folder, parentSessionId, 'subagents', 'agent-' + agentId + '.jsonl');
const filePath = resolveJsonlPath(PROJECTS_DIR, row);

const watchId = ++subagentWatcherSeq;
let offset = 0;
Expand Down Expand Up @@ -1127,7 +1148,7 @@
// WSL profiles only work for plain terminals — Claude CLI sessions need the
// Windows shell because session data lives on the Windows filesystem.
const requestedProfile = resolveShell(effectiveProfileId);
const useWslProfile = isWslShell(requestedProfile.path) && isPlainTerminal;

Check warning on line 1151 in main.js

View workflow job for this annotation

GitHub Actions / test (22)

'useWslProfile' is assigned a value but never used. Allowed unused vars must match /^_/u

Check warning on line 1151 in main.js

View workflow job for this annotation

GitHub Actions / test (20)

'useWslProfile' is assigned a value but never used. Allowed unused vars must match /^_/u
const shellProfile = (isWslShell(requestedProfile.path) && !isPlainTerminal)
? resolveShell('auto')
: requestedProfile;
Expand Down
2 changes: 1 addition & 1 deletion public/jsonl-viewer.js
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,7 @@ const toolRenderers = {
if (!parentSessionId) return el;

// Determine which Nth match this block is for fanout deduplication
const counterKey = parentSessionId + '|' + desc + '|' + type;
const counterKey = JSON.stringify([parentSessionId, desc, type]);
if (agentMatchCounters[counterKey] === undefined) agentMatchCounters[counterKey] = 0;
const matchIndex = agentMatchCounters[counterKey]++;

Expand Down
2 changes: 2 additions & 0 deletions read-session-file.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ const fs = require('fs');
* exactly like top-level sessions (search, archive, rename, etc).
*/
function subagentSessionId(parentSessionId, agentId) {
if (parentSessionId.includes(':')) throw new TypeError(`parentSessionId must not contain ':': ${parentSessionId}`);
if (agentId.includes(':')) throw new TypeError(`agentId must not contain ':': ${agentId}`);
return `sub:${parentSessionId}:${agentId}`;
}

Expand Down
6 changes: 6 additions & 0 deletions test/read-session-file.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,12 @@ test('subagentSessionId formats parent and agent ids into the expected colon-del
assert.equal(parts[2], agent);
});

test('subagentSessionId throws TypeError when agentId or parentSessionId contains a colon', () => {
assert.throws(() => subagentSessionId('parent:bad', 'agent'), TypeError);
assert.throws(() => subagentSessionId('parent', 'agent:bad'), TypeError);
assert.throws(() => subagentSessionId('par:ent', 'age:nt'), TypeError);
});

test('resolveJsonlPath returns top-level path when row has no parent/agent', () => {
const projectsDir = '/projects';
const row = { folder: 'foo', sessionId: 'session-1' };
Expand Down
Loading