@lossless-claude/lcm 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/commands/lcm-compact.md +10 -0
- package/.claude-plugin/commands/lcm-curate.md +16 -6
- package/.claude-plugin/commands/lcm-diagnose.md +10 -0
- package/.claude-plugin/commands/lcm-doctor.md +1 -1
- package/.claude-plugin/commands/lcm-dogfood.md +1 -1
- package/.claude-plugin/commands/lcm-import.md +10 -0
- package/.claude-plugin/commands/lcm-promote.md +10 -0
- package/.claude-plugin/commands/lcm-sensitive.md +10 -0
- package/.claude-plugin/commands/lcm-stats.md +1 -1
- package/.claude-plugin/commands/lcm-status.md +10 -0
- package/.claude-plugin/hooks/README.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +14 -2
- package/.claude-plugin/skills/lcm-context/SKILL.md +10 -0
- package/.claude-plugin/skills/lcm-dogfood/SKILL.md +1 -1
- package/.claude-plugin/skills/lcm-dogfood/references/checks.md +1 -1
- package/README.md +2 -2
- package/dist/bin/lcm.js +828 -474
- package/dist/bin/lcm.js.map +1 -1
- package/dist/installer/install.d.ts +6 -5
- package/dist/installer/install.js +116 -108
- package/dist/installer/install.js.map +1 -1
- package/dist/src/batch-compact.d.ts +7 -1
- package/dist/src/batch-compact.js +80 -6
- package/dist/src/batch-compact.js.map +1 -1
- package/dist/src/bootstrap.d.ts +24 -0
- package/dist/src/bootstrap.js +76 -0
- package/dist/src/bootstrap.js.map +1 -0
- package/dist/src/cli/pipeline-runner.d.ts +47 -0
- package/dist/src/cli/pipeline-runner.js +106 -0
- package/dist/src/cli/pipeline-runner.js.map +1 -0
- package/dist/src/cli/pipeline-step.d.ts +20 -0
- package/dist/src/cli/pipeline-step.js +6 -0
- package/dist/src/cli/pipeline-step.js.map +1 -0
- package/dist/src/cli/progress-state.d.ts +61 -0
- package/dist/src/cli/progress-state.js +16 -0
- package/dist/src/cli/progress-state.js.map +1 -0
- package/dist/src/cli/render-frame.d.ts +26 -0
- package/dist/src/cli/render-frame.js +153 -0
- package/dist/src/cli/render-frame.js.map +1 -0
- package/dist/src/cli/render-summary.d.ts +8 -0
- package/dist/src/cli/render-summary.js +79 -0
- package/dist/src/cli/render-summary.js.map +1 -0
- package/dist/src/cli-help.js +15 -9
- package/dist/src/cli-help.js.map +1 -1
- package/dist/src/codex-transcript.d.ts +50 -0
- package/dist/src/codex-transcript.js +207 -0
- package/dist/src/codex-transcript.js.map +1 -0
- package/dist/src/daemon/auth.d.ts +2 -0
- package/dist/src/daemon/auth.js +40 -0
- package/dist/src/daemon/auth.js.map +1 -0
- package/dist/src/daemon/client.d.ts +5 -1
- package/dist/src/daemon/client.js +21 -2
- package/dist/src/daemon/client.js.map +1 -1
- package/dist/src/daemon/config.d.ts +15 -0
- package/dist/src/daemon/config.js +33 -8
- package/dist/src/daemon/config.js.map +1 -1
- package/dist/src/daemon/content-fence.d.ts +7 -0
- package/dist/src/daemon/content-fence.js +14 -0
- package/dist/src/daemon/content-fence.js.map +1 -0
- package/dist/src/daemon/lifecycle.js +5 -0
- package/dist/src/daemon/lifecycle.js.map +1 -1
- package/dist/src/daemon/orientation.d.ts +1 -0
- package/dist/src/daemon/orientation.js +35 -6
- package/dist/src/daemon/orientation.js.map +1 -1
- package/dist/src/daemon/project.d.ts +1 -0
- package/dist/src/daemon/project.js +95 -3
- package/dist/src/daemon/project.js.map +1 -1
- package/dist/src/daemon/routes/compact.js +41 -10
- package/dist/src/daemon/routes/compact.js.map +1 -1
- package/dist/src/daemon/routes/describe.js +12 -1
- package/dist/src/daemon/routes/describe.js.map +1 -1
- package/dist/src/daemon/routes/expand.js +12 -1
- package/dist/src/daemon/routes/expand.js.map +1 -1
- package/dist/src/daemon/routes/grep.js +11 -2
- package/dist/src/daemon/routes/grep.js.map +1 -1
- package/dist/src/daemon/routes/ingest.js +36 -8
- package/dist/src/daemon/routes/ingest.js.map +1 -1
- package/dist/src/daemon/routes/promote-events.d.ts +3 -0
- package/dist/src/daemon/routes/promote-events.js +180 -0
- package/dist/src/daemon/routes/promote-events.js.map +1 -0
- package/dist/src/daemon/routes/promote.js +18 -3
- package/dist/src/daemon/routes/promote.js.map +1 -1
- package/dist/src/daemon/routes/prompt-search.js +11 -2
- package/dist/src/daemon/routes/prompt-search.js.map +1 -1
- package/dist/src/daemon/routes/recent.js +11 -2
- package/dist/src/daemon/routes/recent.js.map +1 -1
- package/dist/src/daemon/routes/restore.js +117 -69
- package/dist/src/daemon/routes/restore.js.map +1 -1
- package/dist/src/daemon/routes/search.js +12 -1
- package/dist/src/daemon/routes/search.js.map +1 -1
- package/dist/src/daemon/routes/session-complete.d.ts +2 -0
- package/dist/src/daemon/routes/session-complete.js +36 -0
- package/dist/src/daemon/routes/session-complete.js.map +1 -0
- package/dist/src/daemon/routes/status.js +77 -64
- package/dist/src/daemon/routes/status.js.map +1 -1
- package/dist/src/daemon/routes/store.d.ts +2 -1
- package/dist/src/daemon/routes/store.js +42 -8
- package/dist/src/daemon/routes/store.js.map +1 -1
- package/dist/src/daemon/safe-error.d.ts +5 -0
- package/dist/src/daemon/safe-error.js +15 -0
- package/dist/src/daemon/safe-error.js.map +1 -0
- package/dist/src/daemon/server.d.ts +3 -1
- package/dist/src/daemon/server.js +37 -17
- package/dist/src/daemon/server.js.map +1 -1
- package/dist/src/daemon/validate-cwd.d.ts +5 -0
- package/dist/src/daemon/validate-cwd.js +38 -0
- package/dist/src/daemon/validate-cwd.js.map +1 -0
- package/dist/src/daemon/version.d.ts +10 -0
- package/dist/src/daemon/version.js +31 -0
- package/dist/src/daemon/version.js.map +1 -0
- package/dist/src/db/events-path.d.ts +2 -0
- package/dist/src/db/events-path.js +11 -0
- package/dist/src/db/events-path.js.map +1 -0
- package/dist/src/db/events-stats.d.ts +29 -0
- package/dist/src/db/events-stats.js +107 -0
- package/dist/src/db/events-stats.js.map +1 -0
- package/dist/src/db/migration.js +8 -0
- package/dist/src/db/migration.js.map +1 -1
- package/dist/src/diagnose.js +14 -1
- package/dist/src/diagnose.js.map +1 -1
- package/dist/src/doctor/doctor.d.ts +1 -1
- package/dist/src/doctor/doctor.js +184 -15
- package/dist/src/doctor/doctor.js.map +1 -1
- package/dist/src/hooks/auto-heal.js +24 -1
- package/dist/src/hooks/auto-heal.js.map +1 -1
- package/dist/src/hooks/compact.js +7 -0
- package/dist/src/hooks/compact.js.map +1 -1
- package/dist/src/hooks/dispatch.d.ts +1 -1
- package/dist/src/hooks/dispatch.js +19 -1
- package/dist/src/hooks/dispatch.js.map +1 -1
- package/dist/src/hooks/events-db.d.ts +41 -0
- package/dist/src/hooks/events-db.js +190 -0
- package/dist/src/hooks/events-db.js.map +1 -0
- package/dist/src/hooks/extractors.d.ts +17 -0
- package/dist/src/hooks/extractors.js +198 -0
- package/dist/src/hooks/extractors.js.map +1 -0
- package/dist/src/hooks/hook-errors.d.ts +14 -0
- package/dist/src/hooks/hook-errors.js +56 -0
- package/dist/src/hooks/hook-errors.js.map +1 -0
- package/dist/src/hooks/post-tool.d.ts +4 -0
- package/dist/src/hooks/post-tool.js +43 -0
- package/dist/src/hooks/post-tool.js.map +1 -0
- package/dist/src/hooks/restore.js +31 -1
- package/dist/src/hooks/restore.js.map +1 -1
- package/dist/src/hooks/session-end.d.ts +3 -0
- package/dist/src/hooks/session-end.js +68 -5
- package/dist/src/hooks/session-end.js.map +1 -1
- package/dist/src/hooks/session-snapshot.d.ts +12 -0
- package/dist/src/hooks/session-snapshot.js +100 -0
- package/dist/src/hooks/session-snapshot.js.map +1 -0
- package/dist/src/hooks/user-prompt.js +44 -5
- package/dist/src/hooks/user-prompt.js.map +1 -1
- package/dist/src/import-summary.d.ts +4 -0
- package/dist/src/import-summary.js +34 -0
- package/dist/src/import-summary.js.map +1 -0
- package/dist/src/import.d.ts +11 -1
- package/dist/src/import.js +218 -88
- package/dist/src/import.js.map +1 -1
- package/dist/src/installer/settings.d.ts +5 -0
- package/dist/src/installer/settings.js +74 -0
- package/dist/src/installer/settings.js.map +1 -0
- package/dist/src/mcp/server.d.ts +15 -0
- package/dist/src/mcp/server.js +83 -6
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/promotion/detector.js +19 -5
- package/dist/src/promotion/detector.js.map +1 -1
- package/dist/src/scrub.js +26 -0
- package/dist/src/scrub.js.map +1 -1
- package/dist/src/stats.d.ts +4 -0
- package/dist/src/stats.js +23 -0
- package/dist/src/stats.js.map +1 -1
- package/dist/src/store/conversation-store.js +2 -1
- package/dist/src/store/conversation-store.js.map +1 -1
- package/dist/src/store/regex-safety.d.ts +1 -0
- package/dist/src/store/regex-safety.js +22 -0
- package/dist/src/store/regex-safety.js.map +1 -0
- package/dist/src/store/summary-store.js +2 -1
- package/dist/src/store/summary-store.js.map +1 -1
- package/docs/passive-learning.md +138 -0
- package/lcm.mjs +20 -1
- package/package.json +5 -2
package/dist/src/import.js
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import { readdirSync, readFileSync, existsSync,
|
|
1
|
+
import { readdirSync, readFileSync, existsSync, lstatSync } from "node:fs";
|
|
2
2
|
import { join, basename } from "node:path";
|
|
3
3
|
import { homedir } from "node:os";
|
|
4
|
+
import { DatabaseSync } from "node:sqlite";
|
|
5
|
+
import { formatNumber, formatRatio } from "./stats.js";
|
|
6
|
+
import { findAllCodexTranscripts, extractCodexSessionCwd } from "./codex-transcript.js";
|
|
7
|
+
import { projectDbPath, projectId } from "./daemon/project.js";
|
|
4
8
|
export function cwdToProjectHash(cwd) {
|
|
5
9
|
// Claude Code uses the cwd with slashes replaced by dashes, keeping the leading dash
|
|
6
10
|
// e.g. /Users/pedro/Developer/lossless-claude → -Users-pedro-Developer-lossless-claude
|
|
@@ -32,30 +36,65 @@ export function findSessionFiles(projectDir) {
|
|
|
32
36
|
const files = [];
|
|
33
37
|
if (!existsSync(projectDir))
|
|
34
38
|
return files;
|
|
39
|
+
// Track which session IDs have a flat (project-root) transcript so we can
|
|
40
|
+
// deduplicate when the same session also has a nested copy.
|
|
41
|
+
const flatSessionIds = new Set();
|
|
35
42
|
for (const entry of readdirSync(projectDir, { withFileTypes: true })) {
|
|
36
|
-
|
|
43
|
+
// Layout B (flat): <projectDir>/<session-id>.jsonl
|
|
44
|
+
if (entry.isFile() && !entry.isSymbolicLink() && entry.name.endsWith('.jsonl')) {
|
|
37
45
|
try {
|
|
46
|
+
const filePath = join(projectDir, entry.name);
|
|
47
|
+
const st = lstatSync(filePath);
|
|
48
|
+
if (st.isSymbolicLink())
|
|
49
|
+
continue; // skip symlinks
|
|
50
|
+
const sessionId = basename(entry.name, '.jsonl');
|
|
38
51
|
files.push({
|
|
39
|
-
path:
|
|
40
|
-
sessionId
|
|
41
|
-
mtime:
|
|
52
|
+
path: filePath,
|
|
53
|
+
sessionId,
|
|
54
|
+
mtime: st.mtimeMs,
|
|
42
55
|
});
|
|
56
|
+
flatSessionIds.add(sessionId);
|
|
43
57
|
}
|
|
44
58
|
catch {
|
|
45
59
|
// Skip entries that can't be stat'd (file deleted or permissions issue)
|
|
46
60
|
continue;
|
|
47
61
|
}
|
|
48
62
|
}
|
|
49
|
-
if (entry.isDirectory()) {
|
|
63
|
+
if (entry.isDirectory() && !entry.isSymbolicLink()) {
|
|
64
|
+
// Layout A (nested): <projectDir>/<session-id>/<session-id>.jsonl
|
|
65
|
+
const nestedTranscript = join(projectDir, entry.name, `${entry.name}.jsonl`);
|
|
66
|
+
if (existsSync(nestedTranscript)) {
|
|
67
|
+
try {
|
|
68
|
+
const nestedStat = lstatSync(nestedTranscript);
|
|
69
|
+
if (nestedStat.isSymbolicLink()) {
|
|
70
|
+
// skip symlinks
|
|
71
|
+
}
|
|
72
|
+
else if (nestedStat.isFile()) {
|
|
73
|
+
files.push({
|
|
74
|
+
path: nestedTranscript,
|
|
75
|
+
sessionId: entry.name,
|
|
76
|
+
mtime: nestedStat.mtimeMs,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch {
|
|
81
|
+
// Skip entries that can't be stat'd
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
// Subagent transcripts: <projectDir>/<session-id>/subagents/<agent-id>.jsonl
|
|
50
85
|
const subagentsDir = join(projectDir, entry.name, 'subagents');
|
|
51
86
|
if (existsSync(subagentsDir)) {
|
|
52
87
|
for (const sub of readdirSync(subagentsDir, { withFileTypes: true })) {
|
|
53
|
-
if (sub.isFile() && sub.name.endsWith('.jsonl')) {
|
|
88
|
+
if (sub.isFile() && !sub.isSymbolicLink() && sub.name.endsWith('.jsonl')) {
|
|
54
89
|
try {
|
|
90
|
+
const subPath = join(subagentsDir, sub.name);
|
|
91
|
+
const subSt = lstatSync(subPath);
|
|
92
|
+
if (subSt.isSymbolicLink())
|
|
93
|
+
continue; // skip symlinks
|
|
55
94
|
files.push({
|
|
56
|
-
path:
|
|
95
|
+
path: subPath,
|
|
57
96
|
sessionId: basename(sub.name, '.jsonl'),
|
|
58
|
-
mtime:
|
|
97
|
+
mtime: subSt.mtimeMs,
|
|
59
98
|
});
|
|
60
99
|
}
|
|
61
100
|
catch {
|
|
@@ -67,7 +106,16 @@ export function findSessionFiles(projectDir) {
|
|
|
67
106
|
}
|
|
68
107
|
}
|
|
69
108
|
}
|
|
70
|
-
|
|
109
|
+
// Deduplicate: when a session has both a flat and nested transcript,
|
|
110
|
+
// keep only the flat file (the canonical source in newer Claude Code versions).
|
|
111
|
+
// Subagent files (inside subagents/) are kept unconditionally because their
|
|
112
|
+
// paths never match the nested transcript pattern below.
|
|
113
|
+
const nestedSuffix = (sid) => join(sid, `${sid}.jsonl`);
|
|
114
|
+
const deduped = files.filter(f => {
|
|
115
|
+
const isNested = f.path.endsWith(nestedSuffix(f.sessionId));
|
|
116
|
+
return !isNested || !flatSessionIds.has(f.sessionId);
|
|
117
|
+
});
|
|
118
|
+
return deduped.sort((a, b) => {
|
|
71
119
|
const mtimeDiff = a.mtime - b.mtime;
|
|
72
120
|
if (mtimeDiff !== 0)
|
|
73
121
|
return mtimeDiff;
|
|
@@ -77,97 +125,179 @@ export function findSessionFiles(projectDir) {
|
|
|
77
125
|
return a.path.localeCompare(b.path);
|
|
78
126
|
});
|
|
79
127
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const cwd = projectMap.get(entry.name);
|
|
92
|
-
if (!cwd)
|
|
93
|
-
continue;
|
|
94
|
-
projectDirs.push({ dir: join(claudeProjectsDir, entry.name), cwd });
|
|
128
|
+
/**
|
|
129
|
+
* Checks if a session has already been recorded in session_ingest_log,
|
|
130
|
+
* indicating it was fully ingested in a previous run.
|
|
131
|
+
*/
|
|
132
|
+
function isSessionAlreadyIngested(cwd, sessionId, lcmDir) {
|
|
133
|
+
try {
|
|
134
|
+
const dbPath = lcmDir
|
|
135
|
+
? join(lcmDir, "projects", projectId(cwd), "db.sqlite")
|
|
136
|
+
: projectDbPath(cwd);
|
|
137
|
+
if (!existsSync(dbPath)) {
|
|
138
|
+
return false;
|
|
95
139
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
140
|
+
const db = new DatabaseSync(dbPath);
|
|
141
|
+
try {
|
|
142
|
+
db.exec("PRAGMA busy_timeout = 5000");
|
|
143
|
+
const row = db.prepare("SELECT 1 FROM session_ingest_log WHERE session_id = ?").get(sessionId);
|
|
144
|
+
return !!row;
|
|
145
|
+
}
|
|
146
|
+
finally {
|
|
147
|
+
db.close();
|
|
103
148
|
}
|
|
104
149
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
150
|
+
catch {
|
|
151
|
+
// Table may not exist yet or db is inaccessible — proceed with import
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
async function ingestSessionList(client, sessions, options, result) {
|
|
156
|
+
let previousSummary;
|
|
157
|
+
const total = sessions.length;
|
|
158
|
+
for (const { path, sessionId, cwd } of sessions) {
|
|
159
|
+
if (options.dryRun) {
|
|
160
|
+
if (options.verbose) {
|
|
161
|
+
const replayNote = options.replay ? " (would compact)" : "";
|
|
162
|
+
console.log(` [dry-run] ${sessionId}${replayNote}`);
|
|
116
163
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
164
|
+
result.imported++;
|
|
165
|
+
options.onProgress?.({ completed: result.imported + result.skippedEmpty + result.failed, total, current: { sessionId, messages: 0, tokens: 0, startedAt: Date.now() } });
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
// Skip sessions already recorded in session_ingest_log (unless in replay mode,
|
|
169
|
+
// where compaction must still run to keep the temporal chain intact).
|
|
170
|
+
if (!options.replay && isSessionAlreadyIngested(cwd, sessionId, options._lcmDir)) {
|
|
171
|
+
result.skippedEmpty++;
|
|
172
|
+
if (options.verbose)
|
|
173
|
+
console.log(` ↩️ ${sessionId}: already fully ingested`);
|
|
174
|
+
options.onProgress?.({ completed: result.imported + result.skippedEmpty + result.failed, total, current: { sessionId, messages: 0, tokens: 0, startedAt: Date.now() } });
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
const res = await client.post('/ingest', {
|
|
179
|
+
session_id: sessionId,
|
|
180
|
+
cwd,
|
|
181
|
+
transcript_path: path,
|
|
182
|
+
});
|
|
183
|
+
if (res.ingested === 0 && res.totalTokens === 0) {
|
|
184
|
+
result.skippedEmpty++;
|
|
185
|
+
if (options.verbose)
|
|
186
|
+
console.log(` \u23ed\ufe0f ${sessionId}: empty or already ingested`);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
result.imported++;
|
|
190
|
+
result.totalMessages += res.ingested;
|
|
191
|
+
// In replay mode, totalTokens is sourced from compact's tokensBefore to avoid
|
|
192
|
+
// double-counting (compact covers already-ingested sessions too).
|
|
193
|
+
if (!options.replay) {
|
|
194
|
+
result.totalTokens += res.totalTokens;
|
|
133
195
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
196
|
+
if (options.verbose)
|
|
197
|
+
console.log(` \u2705 ${sessionId}: ${res.ingested} messages (${formatNumber(res.totalTokens)} tokens)`);
|
|
198
|
+
}
|
|
199
|
+
// Replay: compact immediately after every session (even already-ingested ones)
|
|
200
|
+
// so that re-runs are idempotent and the temporal chain stays intact.
|
|
201
|
+
if (options.replay) {
|
|
202
|
+
try {
|
|
203
|
+
const compactRes = await client.post('/compact', {
|
|
204
|
+
session_id: sessionId,
|
|
205
|
+
cwd,
|
|
206
|
+
skip_ingest: true,
|
|
207
|
+
client: 'claude',
|
|
208
|
+
...(previousSummary !== undefined ? { previous_summary: previousSummary } : {}),
|
|
209
|
+
});
|
|
210
|
+
const hadPrevious = previousSummary !== undefined;
|
|
211
|
+
if (compactRes.latestSummaryContent !== undefined) {
|
|
212
|
+
previousSummary = compactRes.latestSummaryContent;
|
|
213
|
+
}
|
|
214
|
+
// Use compact's tokensBefore as the authoritative token count for this session.
|
|
215
|
+
// This avoids under-reporting when /ingest returns totalTokens=0 (already-ingested).
|
|
216
|
+
if (typeof compactRes.tokensBefore === 'number') {
|
|
217
|
+
result.totalTokens += compactRes.tokensBefore;
|
|
218
|
+
}
|
|
219
|
+
if (typeof compactRes.tokensAfter === 'number') {
|
|
220
|
+
result.tokensAfter += compactRes.tokensAfter;
|
|
221
|
+
}
|
|
222
|
+
if (options.verbose) {
|
|
223
|
+
const ctx = hadPrevious ? ' (with prior context)' : '';
|
|
224
|
+
if (typeof compactRes.tokensBefore === 'number' && typeof compactRes.tokensAfter === 'number' && compactRes.tokensAfter < compactRes.tokensBefore) {
|
|
225
|
+
const ratio = formatRatio(compactRes.tokensBefore, compactRes.tokensAfter);
|
|
226
|
+
console.log(` \ud83e\udde0 ${sessionId}: ${formatNumber(compactRes.tokensBefore)} \u2192 ${formatNumber(compactRes.tokensAfter)} (${ratio}\u00d7)${ctx}`);
|
|
148
227
|
}
|
|
149
|
-
|
|
150
|
-
const ctx = hadPrevious ? ' (with prior context)' : '';
|
|
228
|
+
else {
|
|
151
229
|
console.log(` \ud83e\udde0 ${sessionId}: compacted${ctx}`);
|
|
152
230
|
}
|
|
153
231
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
}
|
|
232
|
+
}
|
|
233
|
+
catch (err) {
|
|
234
|
+
// Non-fatal: import succeeded; compact failure breaks the chain at this link.
|
|
235
|
+
previousSummary = undefined;
|
|
236
|
+
// Always warn on chain breakage so users know the DAG is incomplete,
|
|
237
|
+
// regardless of whether --verbose was passed.
|
|
238
|
+
console.error(` \u26a0\ufe0f [replay] compact failed for session ${sessionId}: ${err instanceof Error ? err.message : 'unknown error'}`);
|
|
239
|
+
// Fall back to ingest's totalTokens so they aren't silently lost.
|
|
240
|
+
result.totalTokens += res.totalTokens;
|
|
161
241
|
}
|
|
162
242
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
243
|
+
options.onProgress?.({ completed: result.imported + result.skippedEmpty + result.failed, total, current: { sessionId, messages: 0, tokens: 0, startedAt: Date.now() } });
|
|
244
|
+
}
|
|
245
|
+
catch (err) {
|
|
246
|
+
result.failed++;
|
|
247
|
+
if (options.replay)
|
|
248
|
+
previousSummary = undefined; // chain broken by ingest failure
|
|
249
|
+
if (options.verbose)
|
|
250
|
+
console.log(` \u274c ${sessionId}: ${err instanceof Error ? err.message : "failed"}`);
|
|
251
|
+
options.onProgress?.({ completed: result.imported + result.skippedEmpty + result.failed, total, current: { sessionId, messages: 0, tokens: 0, startedAt: Date.now() } });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
// ---------------------------------------------------------------------------
|
|
256
|
+
// Public entry point
|
|
257
|
+
// ---------------------------------------------------------------------------
|
|
258
|
+
export async function importSessions(client, options = {}) {
|
|
259
|
+
const provider = options.provider ?? "claude";
|
|
260
|
+
const result = { imported: 0, skippedEmpty: 0, failed: 0, totalMessages: 0, totalTokens: 0, tokensAfter: 0 };
|
|
261
|
+
// --- Claude Code sessions ---
|
|
262
|
+
if (provider === "claude" || provider === "all") {
|
|
263
|
+
const claudeProjectsDir = options._claudeProjectsDir ?? join(homedir(), '.claude', 'projects');
|
|
264
|
+
const projectDirs = [];
|
|
265
|
+
if (options.all) {
|
|
266
|
+
if (existsSync(claudeProjectsDir)) {
|
|
267
|
+
const projectMap = buildProjectMap(options._lcmDir);
|
|
268
|
+
for (const entry of readdirSync(claudeProjectsDir, { withFileTypes: true })) {
|
|
269
|
+
if (!entry.isDirectory())
|
|
270
|
+
continue;
|
|
271
|
+
const cwd = projectMap.get(entry.name);
|
|
272
|
+
if (!cwd)
|
|
273
|
+
continue;
|
|
274
|
+
projectDirs.push({ dir: join(claudeProjectsDir, entry.name), cwd });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else {
|
|
279
|
+
const cwd = options.cwd ?? process.cwd();
|
|
280
|
+
const hash = cwdToProjectHash(cwd);
|
|
281
|
+
const dir = join(claudeProjectsDir, hash);
|
|
282
|
+
if (existsSync(dir)) {
|
|
283
|
+
projectDirs.push({ dir, cwd });
|
|
169
284
|
}
|
|
170
285
|
}
|
|
286
|
+
for (const { dir, cwd } of projectDirs) {
|
|
287
|
+
const sessionFiles = findSessionFiles(dir);
|
|
288
|
+
await ingestSessionList(client, sessionFiles.map(f => ({ ...f, cwd })), options, result);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// --- Codex CLI sessions ---
|
|
292
|
+
if (provider === "codex" || provider === "all") {
|
|
293
|
+
const codexTranscripts = findAllCodexTranscripts(options._codexDir);
|
|
294
|
+
const codexSessions = codexTranscripts.map(f => ({
|
|
295
|
+
path: f.path,
|
|
296
|
+
sessionId: f.sessionId,
|
|
297
|
+
// Prefer the cwd embedded in the transcript; fall back to process.cwd()
|
|
298
|
+
cwd: extractCodexSessionCwd(f.path) ?? process.cwd(),
|
|
299
|
+
}));
|
|
300
|
+
await ingestSessionList(client, codexSessions, options, result);
|
|
171
301
|
}
|
|
172
302
|
return result;
|
|
173
303
|
}
|
package/dist/src/import.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/import.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"import.js","sourceRoot":"","sources":["../../src/import.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const REQUIRED_HOOKS = [
|
|
2
|
+
{ event: "PostToolUse", command: "lcm post-tool" },
|
|
3
|
+
{ event: "PreCompact", command: "lcm compact --hook" },
|
|
4
|
+
{ event: "SessionStart", command: "lcm restore" },
|
|
5
|
+
{ event: "SessionEnd", command: "lcm session-end" },
|
|
6
|
+
{ event: "UserPromptSubmit", command: "lcm user-prompt" },
|
|
7
|
+
{ event: "Stop", command: "lcm session-snapshot" },
|
|
8
|
+
];
|
|
9
|
+
export function mergeClaudeSettings(existing) {
|
|
10
|
+
const settings = JSON.parse(JSON.stringify(existing));
|
|
11
|
+
settings.hooks = (settings.hooks && typeof settings.hooks === "object" && !Array.isArray(settings.hooks)) ? settings.hooks : {};
|
|
12
|
+
settings.mcpServers = (settings.mcpServers && typeof settings.mcpServers === "object" && !Array.isArray(settings.mcpServers)) ? settings.mcpServers : {};
|
|
13
|
+
// Migrate old hook commands to current form
|
|
14
|
+
const OLD_TO_NEW = {
|
|
15
|
+
"lossless-claude compact": "lcm compact --hook",
|
|
16
|
+
"lossless-claude restore": "lcm restore",
|
|
17
|
+
"lossless-claude session-end": "lcm session-end",
|
|
18
|
+
"lossless-claude user-prompt": "lcm user-prompt",
|
|
19
|
+
// Migrate pre-#90 direct installs that registered without --hook
|
|
20
|
+
"lcm compact": "lcm compact --hook",
|
|
21
|
+
};
|
|
22
|
+
for (const event of Object.keys(settings.hooks)) {
|
|
23
|
+
if (!Array.isArray(settings.hooks[event]))
|
|
24
|
+
continue;
|
|
25
|
+
for (const entry of settings.hooks[event]) {
|
|
26
|
+
if (!Array.isArray(entry.hooks))
|
|
27
|
+
continue;
|
|
28
|
+
for (const h of entry.hooks) {
|
|
29
|
+
if (h.command && OLD_TO_NEW[h.command]) {
|
|
30
|
+
h.command = OLD_TO_NEW[h.command];
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Deduplicate commands within each hook entry after migration
|
|
34
|
+
const seen = new Set();
|
|
35
|
+
entry.hooks = entry.hooks.filter((h) => {
|
|
36
|
+
const key = h.command ?? '';
|
|
37
|
+
if (seen.has(key))
|
|
38
|
+
return false;
|
|
39
|
+
seen.add(key);
|
|
40
|
+
return true;
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
// Remove legacy MCP server entries
|
|
45
|
+
delete settings.mcpServers["lossless-claude"];
|
|
46
|
+
// Remove lcm hooks from settings.json — plugin.json owns them.
|
|
47
|
+
// Having hooks in both causes double-firing.
|
|
48
|
+
for (const { event, command } of REQUIRED_HOOKS) {
|
|
49
|
+
if (!Array.isArray(settings.hooks[event]))
|
|
50
|
+
continue;
|
|
51
|
+
settings.hooks[event] = settings.hooks[event]
|
|
52
|
+
.map((entry) => {
|
|
53
|
+
if (!Array.isArray(entry.hooks))
|
|
54
|
+
return entry;
|
|
55
|
+
return {
|
|
56
|
+
...entry,
|
|
57
|
+
hooks: entry.hooks.filter((h) => h.command !== command),
|
|
58
|
+
};
|
|
59
|
+
})
|
|
60
|
+
.filter((entry) => !Array.isArray(entry.hooks) || entry.hooks.length > 0);
|
|
61
|
+
if (settings.hooks[event].length === 0)
|
|
62
|
+
delete settings.hooks[event];
|
|
63
|
+
}
|
|
64
|
+
if (Object.keys(settings.hooks).length === 0)
|
|
65
|
+
delete settings.hooks;
|
|
66
|
+
// MCP server is now owned by settings.json (written by lcm install / doctor)
|
|
67
|
+
// Do NOT delete mcpServers["lcm"] here — it's managed separately from hooks
|
|
68
|
+
// which are plugin-owned. This prevents the auto-heal loop where doctor adds
|
|
69
|
+
// the MCP entry and hooks cleanup removes it.
|
|
70
|
+
if (Object.keys(settings.mcpServers).length === 0)
|
|
71
|
+
delete settings.mcpServers;
|
|
72
|
+
return settings;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=settings.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"settings.js","sourceRoot":"","sources":["../../../src/installer/settings.ts"],"names":[],"mappings":"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"}
|
package/dist/src/mcp/server.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DaemonClient } from "../daemon/client.js";
|
|
2
|
+
import { ensureDaemon } from "../daemon/lifecycle.js";
|
|
1
3
|
export declare function getMcpToolDefinitions(): {
|
|
2
4
|
name: string;
|
|
3
5
|
description: string;
|
|
@@ -6,4 +8,17 @@ export declare function getMcpToolDefinitions(): {
|
|
|
6
8
|
properties: {};
|
|
7
9
|
};
|
|
8
10
|
}[];
|
|
11
|
+
export type DaemonRequestOpts = {
|
|
12
|
+
port: number;
|
|
13
|
+
pidFilePath: string;
|
|
14
|
+
_ensureDaemon?: typeof ensureDaemon;
|
|
15
|
+
};
|
|
16
|
+
/** Exported for testing. Calls a daemon route with auto-restart + retry on network failure. */
|
|
17
|
+
export declare function handleDaemonRequest(client: Pick<DaemonClient, "post">, route: string, body: Record<string, unknown>, opts: DaemonRequestOpts): Promise<{
|
|
18
|
+
content: Array<{
|
|
19
|
+
type: "text";
|
|
20
|
+
text: string;
|
|
21
|
+
}>;
|
|
22
|
+
isError?: boolean;
|
|
23
|
+
}>;
|
|
9
24
|
export declare function startMcpServer(): Promise<void>;
|
package/dist/src/mcp/server.js
CHANGED
|
@@ -6,6 +6,7 @@ import { homedir } from "node:os";
|
|
|
6
6
|
import { DaemonClient } from "../daemon/client.js";
|
|
7
7
|
import { loadDaemonConfig } from "../daemon/config.js";
|
|
8
8
|
import { ensureDaemon } from "../daemon/lifecycle.js";
|
|
9
|
+
import { PKG_VERSION } from "../daemon/version.js";
|
|
9
10
|
import { lcmGrepTool } from "./tools/lcm-grep.js";
|
|
10
11
|
import { lcmExpandTool } from "./tools/lcm-expand.js";
|
|
11
12
|
import { lcmDescribeTool } from "./tools/lcm-describe.js";
|
|
@@ -38,6 +39,9 @@ const LOCAL_TOOLS = {
|
|
|
38
39
|
lines.push(`| Summaries | ${formatNumber(stats.summaries)} |`);
|
|
39
40
|
lines.push(`| DAG depth | ${stats.maxDepth} |`);
|
|
40
41
|
lines.push(`| Promoted memories | ${stats.promotedCount} |`);
|
|
42
|
+
if (stats.eventsCaptured > 0) {
|
|
43
|
+
lines.push(`| Events | ${formatNumber(stats.eventsCaptured)} captured (${stats.eventsUnprocessed} unprocessed, ${stats.eventsErrors} errors (30d)) |`);
|
|
44
|
+
}
|
|
41
45
|
// Compression section (only when summarization has happened)
|
|
42
46
|
if (stats.summaries > 0) {
|
|
43
47
|
lines.push("");
|
|
@@ -101,26 +105,99 @@ const LOCAL_TOOLS = {
|
|
|
101
105
|
return formatResultsPlain(results);
|
|
102
106
|
},
|
|
103
107
|
};
|
|
108
|
+
// Build per-tool allowlist from tool definitions (keyed by tool name)
|
|
109
|
+
const TOOL_ALLOWED_KEYS = {};
|
|
110
|
+
for (const tool of TOOLS) {
|
|
111
|
+
const props = tool.inputSchema?.properties;
|
|
112
|
+
if (props) {
|
|
113
|
+
TOOL_ALLOWED_KEYS[tool.name] = new Set(Object.keys(props));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
104
116
|
export function getMcpToolDefinitions() { return TOOLS; }
|
|
117
|
+
/** Returns true if the error is a network/connection failure (not a daemon HTTP error). */
|
|
118
|
+
function isNetworkError(err) {
|
|
119
|
+
return err instanceof TypeError;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* One restart attempt per port at a time — concurrent network failures share the same
|
|
123
|
+
* restart promise instead of each spawning a separate daemon process.
|
|
124
|
+
*/
|
|
125
|
+
const restartInFlight = new Map();
|
|
126
|
+
/** Exported for testing. Calls a daemon route with auto-restart + retry on network failure. */
|
|
127
|
+
export async function handleDaemonRequest(client, route, body, opts) {
|
|
128
|
+
let result;
|
|
129
|
+
try {
|
|
130
|
+
result = await client.post(route, body);
|
|
131
|
+
}
|
|
132
|
+
catch (err) {
|
|
133
|
+
// Only retry on network/connection errors, not daemon HTTP errors (4xx/5xx)
|
|
134
|
+
if (!isNetworkError(err)) {
|
|
135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
136
|
+
return { content: [{ type: "text", text: `lcm error: ${msg}` }], isError: true };
|
|
137
|
+
}
|
|
138
|
+
// Daemon crashed — attempt auto-restart then retry once.
|
|
139
|
+
// Coalesce concurrent restart attempts so only one ensureDaemon() runs per port.
|
|
140
|
+
const ensure = opts._ensureDaemon ?? ensureDaemon;
|
|
141
|
+
if (!restartInFlight.has(opts.port)) {
|
|
142
|
+
const p = ensure({ port: opts.port, pidFilePath: opts.pidFilePath, spawnTimeoutMs: 10000 })
|
|
143
|
+
.catch(() => { })
|
|
144
|
+
.finally(() => { restartInFlight.delete(opts.port); });
|
|
145
|
+
restartInFlight.set(opts.port, p);
|
|
146
|
+
}
|
|
147
|
+
await restartInFlight.get(opts.port).catch(() => { });
|
|
148
|
+
try {
|
|
149
|
+
result = await client.post(route, body);
|
|
150
|
+
}
|
|
151
|
+
catch (retryErr) {
|
|
152
|
+
const msg = retryErr instanceof Error ? retryErr.message : String(retryErr);
|
|
153
|
+
return { content: [{ type: "text", text: `lcm daemon unavailable: ${msg}` }], isError: true };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
|
|
157
|
+
}
|
|
105
158
|
export async function startMcpServer() {
|
|
106
159
|
const config = loadDaemonConfig(join(homedir(), ".lossless-claude", "config.json"));
|
|
107
160
|
const port = config.daemon.port;
|
|
108
161
|
const pidFilePath = join(homedir(), ".lossless-claude", "daemon.pid");
|
|
109
|
-
await ensureDaemon({ port, pidFilePath, spawnTimeoutMs: 10000 });
|
|
162
|
+
await ensureDaemon({ port, pidFilePath, spawnTimeoutMs: 10000, expectedVersion: PKG_VERSION });
|
|
110
163
|
const client = new DaemonClient(`http://127.0.0.1:${port}`);
|
|
111
164
|
const server = new Server({ name: "lcm", version: "1.0.0" }, { capabilities: { tools: {} } });
|
|
112
165
|
server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
|
|
113
166
|
server.setRequestHandler(CallToolRequestSchema, async (req) => {
|
|
167
|
+
const rawArgs = req.params.arguments ?? {};
|
|
168
|
+
// Guard: ensure rawArgs is a plain object
|
|
169
|
+
if (typeof rawArgs !== "object" || rawArgs === null || Array.isArray(rawArgs)) {
|
|
170
|
+
return { content: [{ type: "text", text: `Invalid arguments for tool ${req.params.name}: must be an object` }], isError: true };
|
|
171
|
+
}
|
|
172
|
+
const allowedKeys = TOOL_ALLOWED_KEYS[req.params.name];
|
|
173
|
+
const filteredArgs = {};
|
|
174
|
+
if (allowedKeys) {
|
|
175
|
+
for (const key of allowedKeys) {
|
|
176
|
+
if (key in rawArgs)
|
|
177
|
+
filteredArgs[key] = rawArgs[key];
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
// No schema properties defined — default-deny: pass nothing through.
|
|
182
|
+
// This is safer than a denylist-based approach which could miss unknown keys.
|
|
183
|
+
void rawArgs;
|
|
184
|
+
}
|
|
114
185
|
const localHandler = LOCAL_TOOLS[req.params.name];
|
|
115
186
|
if (localHandler) {
|
|
116
|
-
|
|
117
|
-
|
|
187
|
+
try {
|
|
188
|
+
const text = await localHandler(filteredArgs);
|
|
189
|
+
return { content: [{ type: "text", text }] };
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
193
|
+
return { content: [{ type: "text", text: `lcm error: ${msg}` }], isError: true };
|
|
194
|
+
}
|
|
118
195
|
}
|
|
119
196
|
const route = TOOL_ROUTES[req.params.name];
|
|
120
197
|
if (!route)
|
|
121
|
-
|
|
122
|
-
const
|
|
123
|
-
return
|
|
198
|
+
return { content: [{ type: "text", text: `Unknown tool: ${req.params.name}` }], isError: true };
|
|
199
|
+
const body = { ...filteredArgs, cwd: process.env.PWD ?? process.cwd() };
|
|
200
|
+
return handleDaemonRequest(client, route, body, { port, pidFilePath });
|
|
124
201
|
});
|
|
125
202
|
const transport = new StdioServerTransport();
|
|
126
203
|
await server.connect(transport);
|