heyiam 0.3.10 → 0.3.11
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/db.js +51 -3
- package/dist/format-utils.js +19 -0
- package/dist/sync.js +3 -13
- package/package.json +1 -1
package/dist/db.js
CHANGED
|
@@ -5,6 +5,7 @@ import crypto from 'node:crypto';
|
|
|
5
5
|
import { mkdirSync, statSync } from 'node:fs';
|
|
6
6
|
import { join } from 'node:path';
|
|
7
7
|
import { homedir } from 'node:os';
|
|
8
|
+
import { projectDirFromPath } from './format-utils.js';
|
|
8
9
|
// ── Constants ────────────────────────────────────────────────
|
|
9
10
|
function getDataDir() {
|
|
10
11
|
return process.env.HEYIAM_DATA_DIR || join(homedir(), '.local', 'share', 'heyiam');
|
|
@@ -12,7 +13,7 @@ function getDataDir() {
|
|
|
12
13
|
export function getDbPath() {
|
|
13
14
|
return join(getDataDir(), 'sessions.db');
|
|
14
15
|
}
|
|
15
|
-
const CURRENT_SCHEMA_VERSION =
|
|
16
|
+
const CURRENT_SCHEMA_VERSION = 6;
|
|
16
17
|
// ── Singleton ────────────────────────────────────────────────
|
|
17
18
|
let _db = null;
|
|
18
19
|
export function getDatabase(dbPath = getDbPath()) {
|
|
@@ -53,6 +54,9 @@ function runMigrations(db) {
|
|
|
53
54
|
if (currentVersion < 5) {
|
|
54
55
|
migrateToV5(db);
|
|
55
56
|
}
|
|
57
|
+
if (currentVersion < 6) {
|
|
58
|
+
migrateToV6(db);
|
|
59
|
+
}
|
|
56
60
|
}
|
|
57
61
|
function migrateToV2(db) {
|
|
58
62
|
// Drop old tables if upgrading from v1
|
|
@@ -169,14 +173,48 @@ function migrateToV4(db) {
|
|
|
169
173
|
});
|
|
170
174
|
tx();
|
|
171
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* One-time repair: backfill rows where project_dir drifted from file_path.
|
|
178
|
+
*
|
|
179
|
+
* Claude Code v2.1+ relocates a session to a new project dir when the cwd
|
|
180
|
+
* changes mid-session. Earlier versions of this CLI healed file_path in
|
|
181
|
+
* isolation, leaving project_dir pointing at the original directory and
|
|
182
|
+
* making sessions appear under the wrong project in the UI.
|
|
183
|
+
*/
|
|
184
|
+
function migrateToV6(db) {
|
|
185
|
+
const tx = db.transaction(() => {
|
|
186
|
+
const rows = db.prepare('SELECT id, file_path, is_subagent, project_dir FROM sessions WHERE file_path IS NOT NULL').all();
|
|
187
|
+
const update = db.prepare('UPDATE sessions SET project_dir = ? WHERE id = ?');
|
|
188
|
+
let healed = 0;
|
|
189
|
+
for (const r of rows) {
|
|
190
|
+
if (r.file_path.includes('://'))
|
|
191
|
+
continue;
|
|
192
|
+
const derived = projectDirFromPath(r.file_path, r.is_subagent === 1);
|
|
193
|
+
if (derived && derived !== r.project_dir) {
|
|
194
|
+
update.run(derived, r.id);
|
|
195
|
+
healed++;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (healed > 0) {
|
|
199
|
+
console.log('[migrate v6] Repaired project_dir for ' + healed + ' session(s)');
|
|
200
|
+
}
|
|
201
|
+
db.prepare('UPDATE schema_version SET version = ?').run(6);
|
|
202
|
+
});
|
|
203
|
+
tx();
|
|
204
|
+
}
|
|
172
205
|
// ── Staleness Check ──────────────────────────────────────────
|
|
173
206
|
export function isSessionStale(db, sessionId, filePath) {
|
|
174
|
-
const row = db.prepare('SELECT file_mtime, file_size FROM sessions WHERE id = ?').get(sessionId);
|
|
207
|
+
const row = db.prepare('SELECT file_mtime, file_size, file_path FROM sessions WHERE id = ?').get(sessionId);
|
|
175
208
|
if (!row)
|
|
176
209
|
return true; // Not in DB — needs indexing
|
|
177
210
|
// Skip stat for non-filesystem paths (e.g. cursor:// URLs) — F23 fix
|
|
178
211
|
if (filePath.includes('://'))
|
|
179
212
|
return true;
|
|
213
|
+
// Claude Code v2.1+ renames a session file when cwd changes mid-session.
|
|
214
|
+
// A rename preserves mtime/size, so without this check the row's stale
|
|
215
|
+
// project_dir would never get refreshed.
|
|
216
|
+
if (row.file_path && row.file_path !== filePath)
|
|
217
|
+
return true;
|
|
180
218
|
try {
|
|
181
219
|
const stat = statSync(filePath);
|
|
182
220
|
// F21: Use Math.floor to avoid floating-point comparison issues
|
|
@@ -379,9 +417,19 @@ export function deleteSession(db, sessionId) {
|
|
|
379
417
|
* we discover the originally-indexed path has been deleted by its source
|
|
380
418
|
* tool (e.g., Claude Code's 30-day cleanup) and we've fallen back to an
|
|
381
419
|
* archived copy.
|
|
420
|
+
*
|
|
421
|
+
* Also re-derives project_dir from the new path. Claude Code v2.1+ relocates
|
|
422
|
+
* sessions when the cwd changes mid-session, and project_dir is a denormalized
|
|
423
|
+
* cache of dirname(file_path) — they must move together or the UI groups
|
|
424
|
+
* sessions under their old project.
|
|
382
425
|
*/
|
|
383
426
|
export function updateSessionPath(db, sessionId, newPath) {
|
|
384
|
-
db.prepare('
|
|
427
|
+
const row = db.prepare('SELECT is_subagent FROM sessions WHERE id = ?').get(sessionId);
|
|
428
|
+
if (!row)
|
|
429
|
+
return;
|
|
430
|
+
const projectDir = projectDirFromPath(newPath, row.is_subagent === 1);
|
|
431
|
+
db.prepare('UPDATE sessions SET file_path = ?, project_dir = ? WHERE id = ?')
|
|
432
|
+
.run(newPath, projectDir, sessionId);
|
|
385
433
|
}
|
|
386
434
|
// ── Rebuild Index ────────────────────────────────────────────
|
|
387
435
|
export function rebuildIndex(db, onProgress) {
|
package/dist/format-utils.js
CHANGED
|
@@ -1,7 +1,26 @@
|
|
|
1
|
+
import { basename, dirname } from 'node:path';
|
|
1
2
|
/** Escape HTML special characters for safe embedding. */
|
|
2
3
|
export function escapeHtml(str) {
|
|
3
4
|
return str.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
4
5
|
}
|
|
6
|
+
/**
|
|
7
|
+
* Derive the project_dir segment from a Claude Code session file path.
|
|
8
|
+
*
|
|
9
|
+
* Layouts:
|
|
10
|
+
* .../projects/{projectDir}/{sessionId}.jsonl — parent session
|
|
11
|
+
* .../projects/{projectDir}/{parentSessionId}/subagents/X.jsonl — subagent
|
|
12
|
+
*
|
|
13
|
+
* Claude Code v2.1+ relocates session files when cwd changes mid-session,
|
|
14
|
+
* so we need to re-derive project_dir from the current file path rather than
|
|
15
|
+
* trusting a value stored at first-index time.
|
|
16
|
+
*/
|
|
17
|
+
export function projectDirFromPath(filePath, isSubagent) {
|
|
18
|
+
if (isSubagent) {
|
|
19
|
+
const sessionDataDir = dirname(dirname(filePath));
|
|
20
|
+
return basename(dirname(sessionDataDir));
|
|
21
|
+
}
|
|
22
|
+
return basename(dirname(filePath));
|
|
23
|
+
}
|
|
5
24
|
/** Format LOC as human-readable (e.g. "2.4k"). */
|
|
6
25
|
export function formatLoc(loc) {
|
|
7
26
|
if (loc >= 1000)
|
package/dist/sync.js
CHANGED
|
@@ -12,7 +12,7 @@ import { analyzeSession } from './analyzer.js';
|
|
|
12
12
|
import { isSessionStale, indexSession, rebuildIndex, optimizeFtsIndex, } from './db.js';
|
|
13
13
|
import { renderCompact } from './context-export.js';
|
|
14
14
|
import { getArchiveDir } from './settings.js';
|
|
15
|
-
import { displayNameFromDir } from './format-utils.js';
|
|
15
|
+
import { displayNameFromDir, projectDirFromPath } from './format-utils.js';
|
|
16
16
|
export { displayNameFromDir } from './format-utils.js';
|
|
17
17
|
let _syncState = {
|
|
18
18
|
status: 'idle',
|
|
@@ -216,18 +216,8 @@ async function handleFileChange(db, filePath) {
|
|
|
216
216
|
const parentDirName = basename(dirname(filePath));
|
|
217
217
|
// Detect subagent: path contains /subagents/ directory
|
|
218
218
|
const isSubagent = parentDirName === 'subagents';
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
if (isSubagent) {
|
|
222
|
-
// .../projects/{projectDir}/{parentSessionId}/subagents/{agentId}.jsonl
|
|
223
|
-
const sessionDataDir = dirname(dirname(filePath)); // up past subagents/
|
|
224
|
-
parentSessionId = basename(sessionDataDir);
|
|
225
|
-
projectDir = basename(dirname(sessionDataDir));
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
// .../projects/{projectDir}/{sessionId}.jsonl
|
|
229
|
-
projectDir = parentDirName;
|
|
230
|
-
}
|
|
219
|
+
const projectDir = projectDirFromPath(filePath, isSubagent);
|
|
220
|
+
const parentSessionId = isSubagent ? basename(dirname(dirname(filePath))) : undefined;
|
|
231
221
|
const projectName = displayNameFromDir(projectDir);
|
|
232
222
|
const meta = {
|
|
233
223
|
path: filePath,
|