@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.
Files changed (182) hide show
  1. package/.claude-plugin/commands/lcm-compact.md +10 -0
  2. package/.claude-plugin/commands/lcm-curate.md +16 -6
  3. package/.claude-plugin/commands/lcm-diagnose.md +10 -0
  4. package/.claude-plugin/commands/lcm-doctor.md +1 -1
  5. package/.claude-plugin/commands/lcm-dogfood.md +1 -1
  6. package/.claude-plugin/commands/lcm-import.md +10 -0
  7. package/.claude-plugin/commands/lcm-promote.md +10 -0
  8. package/.claude-plugin/commands/lcm-sensitive.md +10 -0
  9. package/.claude-plugin/commands/lcm-stats.md +1 -1
  10. package/.claude-plugin/commands/lcm-status.md +10 -0
  11. package/.claude-plugin/hooks/README.md +1 -1
  12. package/.claude-plugin/marketplace.json +1 -1
  13. package/.claude-plugin/plugin.json +14 -2
  14. package/.claude-plugin/skills/lcm-context/SKILL.md +10 -0
  15. package/.claude-plugin/skills/lcm-dogfood/SKILL.md +1 -1
  16. package/.claude-plugin/skills/lcm-dogfood/references/checks.md +1 -1
  17. package/README.md +2 -2
  18. package/dist/bin/lcm.js +828 -474
  19. package/dist/bin/lcm.js.map +1 -1
  20. package/dist/installer/install.d.ts +6 -5
  21. package/dist/installer/install.js +116 -108
  22. package/dist/installer/install.js.map +1 -1
  23. package/dist/src/batch-compact.d.ts +7 -1
  24. package/dist/src/batch-compact.js +80 -6
  25. package/dist/src/batch-compact.js.map +1 -1
  26. package/dist/src/bootstrap.d.ts +24 -0
  27. package/dist/src/bootstrap.js +76 -0
  28. package/dist/src/bootstrap.js.map +1 -0
  29. package/dist/src/cli/pipeline-runner.d.ts +47 -0
  30. package/dist/src/cli/pipeline-runner.js +106 -0
  31. package/dist/src/cli/pipeline-runner.js.map +1 -0
  32. package/dist/src/cli/pipeline-step.d.ts +20 -0
  33. package/dist/src/cli/pipeline-step.js +6 -0
  34. package/dist/src/cli/pipeline-step.js.map +1 -0
  35. package/dist/src/cli/progress-state.d.ts +61 -0
  36. package/dist/src/cli/progress-state.js +16 -0
  37. package/dist/src/cli/progress-state.js.map +1 -0
  38. package/dist/src/cli/render-frame.d.ts +26 -0
  39. package/dist/src/cli/render-frame.js +153 -0
  40. package/dist/src/cli/render-frame.js.map +1 -0
  41. package/dist/src/cli/render-summary.d.ts +8 -0
  42. package/dist/src/cli/render-summary.js +79 -0
  43. package/dist/src/cli/render-summary.js.map +1 -0
  44. package/dist/src/cli-help.js +15 -9
  45. package/dist/src/cli-help.js.map +1 -1
  46. package/dist/src/codex-transcript.d.ts +50 -0
  47. package/dist/src/codex-transcript.js +207 -0
  48. package/dist/src/codex-transcript.js.map +1 -0
  49. package/dist/src/daemon/auth.d.ts +2 -0
  50. package/dist/src/daemon/auth.js +40 -0
  51. package/dist/src/daemon/auth.js.map +1 -0
  52. package/dist/src/daemon/client.d.ts +5 -1
  53. package/dist/src/daemon/client.js +21 -2
  54. package/dist/src/daemon/client.js.map +1 -1
  55. package/dist/src/daemon/config.d.ts +15 -0
  56. package/dist/src/daemon/config.js +33 -8
  57. package/dist/src/daemon/config.js.map +1 -1
  58. package/dist/src/daemon/content-fence.d.ts +7 -0
  59. package/dist/src/daemon/content-fence.js +14 -0
  60. package/dist/src/daemon/content-fence.js.map +1 -0
  61. package/dist/src/daemon/lifecycle.js +5 -0
  62. package/dist/src/daemon/lifecycle.js.map +1 -1
  63. package/dist/src/daemon/orientation.d.ts +1 -0
  64. package/dist/src/daemon/orientation.js +35 -6
  65. package/dist/src/daemon/orientation.js.map +1 -1
  66. package/dist/src/daemon/project.d.ts +1 -0
  67. package/dist/src/daemon/project.js +95 -3
  68. package/dist/src/daemon/project.js.map +1 -1
  69. package/dist/src/daemon/routes/compact.js +41 -10
  70. package/dist/src/daemon/routes/compact.js.map +1 -1
  71. package/dist/src/daemon/routes/describe.js +12 -1
  72. package/dist/src/daemon/routes/describe.js.map +1 -1
  73. package/dist/src/daemon/routes/expand.js +12 -1
  74. package/dist/src/daemon/routes/expand.js.map +1 -1
  75. package/dist/src/daemon/routes/grep.js +11 -2
  76. package/dist/src/daemon/routes/grep.js.map +1 -1
  77. package/dist/src/daemon/routes/ingest.js +36 -8
  78. package/dist/src/daemon/routes/ingest.js.map +1 -1
  79. package/dist/src/daemon/routes/promote-events.d.ts +3 -0
  80. package/dist/src/daemon/routes/promote-events.js +180 -0
  81. package/dist/src/daemon/routes/promote-events.js.map +1 -0
  82. package/dist/src/daemon/routes/promote.js +18 -3
  83. package/dist/src/daemon/routes/promote.js.map +1 -1
  84. package/dist/src/daemon/routes/prompt-search.js +11 -2
  85. package/dist/src/daemon/routes/prompt-search.js.map +1 -1
  86. package/dist/src/daemon/routes/recent.js +11 -2
  87. package/dist/src/daemon/routes/recent.js.map +1 -1
  88. package/dist/src/daemon/routes/restore.js +117 -69
  89. package/dist/src/daemon/routes/restore.js.map +1 -1
  90. package/dist/src/daemon/routes/search.js +12 -1
  91. package/dist/src/daemon/routes/search.js.map +1 -1
  92. package/dist/src/daemon/routes/session-complete.d.ts +2 -0
  93. package/dist/src/daemon/routes/session-complete.js +36 -0
  94. package/dist/src/daemon/routes/session-complete.js.map +1 -0
  95. package/dist/src/daemon/routes/status.js +77 -64
  96. package/dist/src/daemon/routes/status.js.map +1 -1
  97. package/dist/src/daemon/routes/store.d.ts +2 -1
  98. package/dist/src/daemon/routes/store.js +42 -8
  99. package/dist/src/daemon/routes/store.js.map +1 -1
  100. package/dist/src/daemon/safe-error.d.ts +5 -0
  101. package/dist/src/daemon/safe-error.js +15 -0
  102. package/dist/src/daemon/safe-error.js.map +1 -0
  103. package/dist/src/daemon/server.d.ts +3 -1
  104. package/dist/src/daemon/server.js +37 -17
  105. package/dist/src/daemon/server.js.map +1 -1
  106. package/dist/src/daemon/validate-cwd.d.ts +5 -0
  107. package/dist/src/daemon/validate-cwd.js +38 -0
  108. package/dist/src/daemon/validate-cwd.js.map +1 -0
  109. package/dist/src/daemon/version.d.ts +10 -0
  110. package/dist/src/daemon/version.js +31 -0
  111. package/dist/src/daemon/version.js.map +1 -0
  112. package/dist/src/db/events-path.d.ts +2 -0
  113. package/dist/src/db/events-path.js +11 -0
  114. package/dist/src/db/events-path.js.map +1 -0
  115. package/dist/src/db/events-stats.d.ts +29 -0
  116. package/dist/src/db/events-stats.js +107 -0
  117. package/dist/src/db/events-stats.js.map +1 -0
  118. package/dist/src/db/migration.js +8 -0
  119. package/dist/src/db/migration.js.map +1 -1
  120. package/dist/src/diagnose.js +14 -1
  121. package/dist/src/diagnose.js.map +1 -1
  122. package/dist/src/doctor/doctor.d.ts +1 -1
  123. package/dist/src/doctor/doctor.js +184 -15
  124. package/dist/src/doctor/doctor.js.map +1 -1
  125. package/dist/src/hooks/auto-heal.js +24 -1
  126. package/dist/src/hooks/auto-heal.js.map +1 -1
  127. package/dist/src/hooks/compact.js +7 -0
  128. package/dist/src/hooks/compact.js.map +1 -1
  129. package/dist/src/hooks/dispatch.d.ts +1 -1
  130. package/dist/src/hooks/dispatch.js +19 -1
  131. package/dist/src/hooks/dispatch.js.map +1 -1
  132. package/dist/src/hooks/events-db.d.ts +41 -0
  133. package/dist/src/hooks/events-db.js +190 -0
  134. package/dist/src/hooks/events-db.js.map +1 -0
  135. package/dist/src/hooks/extractors.d.ts +17 -0
  136. package/dist/src/hooks/extractors.js +198 -0
  137. package/dist/src/hooks/extractors.js.map +1 -0
  138. package/dist/src/hooks/hook-errors.d.ts +14 -0
  139. package/dist/src/hooks/hook-errors.js +56 -0
  140. package/dist/src/hooks/hook-errors.js.map +1 -0
  141. package/dist/src/hooks/post-tool.d.ts +4 -0
  142. package/dist/src/hooks/post-tool.js +43 -0
  143. package/dist/src/hooks/post-tool.js.map +1 -0
  144. package/dist/src/hooks/restore.js +31 -1
  145. package/dist/src/hooks/restore.js.map +1 -1
  146. package/dist/src/hooks/session-end.d.ts +3 -0
  147. package/dist/src/hooks/session-end.js +68 -5
  148. package/dist/src/hooks/session-end.js.map +1 -1
  149. package/dist/src/hooks/session-snapshot.d.ts +12 -0
  150. package/dist/src/hooks/session-snapshot.js +100 -0
  151. package/dist/src/hooks/session-snapshot.js.map +1 -0
  152. package/dist/src/hooks/user-prompt.js +44 -5
  153. package/dist/src/hooks/user-prompt.js.map +1 -1
  154. package/dist/src/import-summary.d.ts +4 -0
  155. package/dist/src/import-summary.js +34 -0
  156. package/dist/src/import-summary.js.map +1 -0
  157. package/dist/src/import.d.ts +11 -1
  158. package/dist/src/import.js +218 -88
  159. package/dist/src/import.js.map +1 -1
  160. package/dist/src/installer/settings.d.ts +5 -0
  161. package/dist/src/installer/settings.js +74 -0
  162. package/dist/src/installer/settings.js.map +1 -0
  163. package/dist/src/mcp/server.d.ts +15 -0
  164. package/dist/src/mcp/server.js +83 -6
  165. package/dist/src/mcp/server.js.map +1 -1
  166. package/dist/src/promotion/detector.js +19 -5
  167. package/dist/src/promotion/detector.js.map +1 -1
  168. package/dist/src/scrub.js +26 -0
  169. package/dist/src/scrub.js.map +1 -1
  170. package/dist/src/stats.d.ts +4 -0
  171. package/dist/src/stats.js +23 -0
  172. package/dist/src/stats.js.map +1 -1
  173. package/dist/src/store/conversation-store.js +2 -1
  174. package/dist/src/store/conversation-store.js.map +1 -1
  175. package/dist/src/store/regex-safety.d.ts +1 -0
  176. package/dist/src/store/regex-safety.js +22 -0
  177. package/dist/src/store/regex-safety.js.map +1 -0
  178. package/dist/src/store/summary-store.js +2 -1
  179. package/dist/src/store/summary-store.js.map +1 -1
  180. package/docs/passive-learning.md +138 -0
  181. package/lcm.mjs +20 -1
  182. package/package.json +5 -2
@@ -0,0 +1,190 @@
1
+ // src/hooks/events-db.ts
2
+ import { DatabaseSync } from "node:sqlite";
3
+ import { mkdirSync } from "node:fs";
4
+ import { dirname } from "node:path";
5
+ const SCHEMA_VERSION = 2;
6
+ const SCHEMA_SQL = `
7
+ CREATE TABLE IF NOT EXISTS schema_version (version INTEGER NOT NULL);
8
+ CREATE TABLE IF NOT EXISTS events (
9
+ event_id INTEGER PRIMARY KEY AUTOINCREMENT,
10
+ session_id TEXT NOT NULL,
11
+ seq INTEGER NOT NULL DEFAULT 0,
12
+ type TEXT NOT NULL,
13
+ category TEXT NOT NULL,
14
+ data TEXT NOT NULL,
15
+ priority INTEGER DEFAULT 3,
16
+ source_hook TEXT NOT NULL,
17
+ prev_event_id INTEGER,
18
+ processed_at TEXT,
19
+ created_at TEXT DEFAULT (datetime('now'))
20
+ );
21
+ CREATE INDEX IF NOT EXISTS idx_events_unprocessed ON events(processed_at) WHERE processed_at IS NULL;
22
+ CREATE INDEX IF NOT EXISTS idx_events_session ON events(session_id, created_at);
23
+ CREATE TABLE IF NOT EXISTS error_log (
24
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
25
+ hook TEXT NOT NULL,
26
+ error TEXT NOT NULL,
27
+ session_id TEXT,
28
+ created_at TEXT DEFAULT (datetime('now'))
29
+ );
30
+ CREATE INDEX IF NOT EXISTS idx_error_log_created ON error_log(created_at);
31
+ `;
32
+ export class EventsDb {
33
+ db;
34
+ constructor(dbPath) {
35
+ mkdirSync(dirname(dbPath), { recursive: true, mode: 0o700 });
36
+ this.db = new DatabaseSync(dbPath);
37
+ this.db.exec("PRAGMA journal_mode = WAL");
38
+ this.db.exec("PRAGMA busy_timeout = 5000");
39
+ this.migrate();
40
+ }
41
+ migrate() {
42
+ // Check if schema_version table exists
43
+ const row = this.db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='schema_version'").get();
44
+ if (!row) {
45
+ this.db.exec(SCHEMA_SQL);
46
+ this.db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
47
+ return;
48
+ }
49
+ const versionRow = this.db.prepare("SELECT version FROM schema_version").get();
50
+ // Handle empty schema_version table (table exists but has no rows)
51
+ if (!versionRow) {
52
+ this.db.prepare("INSERT INTO schema_version (version) VALUES (?)").run(SCHEMA_VERSION);
53
+ // Ensure v2 tables exist even in this edge case
54
+ this.db.exec(`
55
+ CREATE TABLE IF NOT EXISTS error_log (
56
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
57
+ hook TEXT NOT NULL,
58
+ error TEXT NOT NULL,
59
+ session_id TEXT,
60
+ created_at TEXT DEFAULT (datetime('now'))
61
+ );
62
+ CREATE INDEX IF NOT EXISTS idx_error_log_created ON error_log(created_at);
63
+ `);
64
+ return;
65
+ }
66
+ const currentVersion = versionRow.version;
67
+ if (currentVersion < 2) {
68
+ this.db.exec("BEGIN EXCLUSIVE");
69
+ try {
70
+ this.db.exec(`
71
+ CREATE TABLE IF NOT EXISTS error_log (
72
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
73
+ hook TEXT NOT NULL,
74
+ error TEXT NOT NULL,
75
+ session_id TEXT,
76
+ created_at TEXT DEFAULT (datetime('now'))
77
+ );
78
+ CREATE INDEX IF NOT EXISTS idx_error_log_created ON error_log(created_at);
79
+ `);
80
+ this.db.prepare("UPDATE schema_version SET version = 2").run();
81
+ this.db.exec("COMMIT");
82
+ }
83
+ catch (e) {
84
+ try {
85
+ this.db.exec("ROLLBACK");
86
+ }
87
+ catch { /* ignore */ }
88
+ throw e;
89
+ }
90
+ }
91
+ }
92
+ insertEvent(sessionId, event, sourceHook) {
93
+ const stmt = this.db.prepare(`
94
+ INSERT INTO events (session_id, seq, type, category, data, priority, source_hook)
95
+ VALUES (?, (SELECT COALESCE(MAX(seq), 0) + 1 FROM events WHERE session_id = ?),
96
+ ?, ?, ?, ?, ?)
97
+ `);
98
+ const result = stmt.run(sessionId, sessionId, event.type, event.category, event.data, event.priority, sourceHook);
99
+ return Number(result.lastInsertRowid);
100
+ }
101
+ getUnprocessed(limit = 500) {
102
+ return this.db.prepare("SELECT * FROM events WHERE processed_at IS NULL ORDER BY session_id, seq LIMIT ?").all(limit);
103
+ }
104
+ markProcessed(eventIds) {
105
+ if (eventIds.length === 0)
106
+ return;
107
+ const placeholders = eventIds.map(() => "?").join(",");
108
+ this.db.prepare(`UPDATE events SET processed_at = datetime('now') WHERE event_id IN (${placeholders})`).run(...eventIds);
109
+ }
110
+ pruneProcessed(olderThanDays) {
111
+ const result = this.db.prepare(`DELETE FROM events WHERE processed_at IS NOT NULL
112
+ AND processed_at < datetime('now', '-' || ? || ' days')`).run(olderThanDays);
113
+ return Number(result.changes);
114
+ }
115
+ setPrevEventId(eventId, prevEventId) {
116
+ this.db.prepare("UPDATE events SET prev_event_id = ? WHERE event_id = ?")
117
+ .run(prevEventId, eventId);
118
+ }
119
+ logHookError(hook, error, sessionId) {
120
+ const msg = error instanceof Error ? error.message : String(error);
121
+ this.db.prepare("INSERT INTO error_log (hook, error, session_id) VALUES (?, ?, ?)").run(hook, msg, sessionId ?? null);
122
+ }
123
+ getHealthStats() {
124
+ const eventTotals = this.db.prepare("SELECT COUNT(*) as totalEvents, MAX(created_at) as lastCapture FROM events").get();
125
+ const unprocessedRow = this.db.prepare("SELECT COUNT(*) as unprocessed FROM events WHERE processed_at IS NULL").get();
126
+ const errorTotals = this.db.prepare("SELECT COUNT(*) as errors, MAX(created_at) as lastError FROM error_log WHERE hook NOT LIKE 'maintenance:%' AND created_at >= datetime('now', '-30 days')").get();
127
+ return {
128
+ totalEvents: eventTotals.totalEvents,
129
+ unprocessed: unprocessedRow.unprocessed,
130
+ errors: errorTotals.errors,
131
+ lastCapture: eventTotals.lastCapture,
132
+ lastError: errorTotals.lastError,
133
+ };
134
+ }
135
+ pruneUnprocessed(maxRows = 10_000, maxAgeDays = 30) {
136
+ let pruned = 0;
137
+ this.db.exec("BEGIN");
138
+ try {
139
+ const ageCountRow = this.db.prepare(`SELECT COUNT(*) as c FROM events
140
+ WHERE processed_at IS NULL
141
+ AND created_at < datetime('now', '-' || ? || ' days')`).get(maxAgeDays);
142
+ const ageCount = ageCountRow.c;
143
+ const totalUnprocessedRow = this.db.prepare("SELECT COUNT(*) as c FROM events WHERE processed_at IS NULL").get();
144
+ const totalUnprocessed = totalUnprocessedRow.c;
145
+ const remainingAfterAge = totalUnprocessed - ageCount;
146
+ let excess = 0;
147
+ if (remainingAfterAge > maxRows) {
148
+ excess = remainingAfterAge - maxRows;
149
+ }
150
+ pruned = ageCount + excess;
151
+ if (pruned > 0) {
152
+ this.db.prepare("INSERT INTO error_log (hook, error, session_id) VALUES (?, ?, NULL)").run("maintenance:pruneUnprocessed", `pruned ${pruned} unprocessed events (age/cap limit)`);
153
+ }
154
+ if (ageCount > 0) {
155
+ this.db.prepare(`DELETE FROM events WHERE processed_at IS NULL
156
+ AND event_id IN (
157
+ SELECT event_id FROM events WHERE processed_at IS NULL
158
+ AND created_at < datetime('now', '-' || ? || ' days')
159
+ )`).run(maxAgeDays);
160
+ }
161
+ if (excess > 0) {
162
+ this.db.prepare(`DELETE FROM events WHERE event_id IN (
163
+ SELECT event_id FROM events WHERE processed_at IS NULL
164
+ ORDER BY event_id ASC LIMIT ?
165
+ )`).run(excess);
166
+ }
167
+ this.db.exec("COMMIT");
168
+ }
169
+ catch (e) {
170
+ try {
171
+ this.db.exec("ROLLBACK");
172
+ }
173
+ catch { /* ignore */ }
174
+ throw e;
175
+ }
176
+ return { pruned };
177
+ }
178
+ pruneErrorLog(olderThanDays = 30) {
179
+ const result = this.db.prepare("DELETE FROM error_log WHERE created_at < datetime('now', '-' || ? || ' days')").run(olderThanDays);
180
+ return Number(result.changes);
181
+ }
182
+ /** Expose raw DB for testing only. */
183
+ raw() {
184
+ return this.db;
185
+ }
186
+ close() {
187
+ this.db.close();
188
+ }
189
+ }
190
+ //# sourceMappingURL=events-db.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events-db.js","sourceRoot":"","sources":["../../../src/hooks/events-db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface ExtractedEvent {
2
+ type: string;
3
+ category: string;
4
+ data: string;
5
+ priority: number;
6
+ }
7
+ interface PostToolInput {
8
+ tool_name: string;
9
+ tool_input: Record<string, unknown>;
10
+ tool_response?: unknown;
11
+ tool_output?: {
12
+ isError?: boolean;
13
+ };
14
+ }
15
+ export declare function extractPostToolEvents(input: PostToolInput): ExtractedEvent[];
16
+ export declare function extractUserPromptEvents(prompt: string): ExtractedEvent[];
17
+ export {};
@@ -0,0 +1,198 @@
1
+ // src/hooks/extractors.ts
2
+ const SENSITIVE_PATHS = [".env", ".ssh/", "credentials", "secrets/", ".npmrc", ".netrc"];
3
+ const DATA_SOFT_CAP = 2000;
4
+ const NEGATIVE_PATTERNS = [
5
+ "don't worry", "dont worry",
6
+ "never mind", "nevermind",
7
+ "not sure", "no idea",
8
+ "doesn't matter", "doesnt matter", "does not matter",
9
+ "forget about", "forget it",
10
+ "no preference", "whatever you think",
11
+ "up to you",
12
+ ];
13
+ const ENV_COMMANDS = ["npm install", "npm i ", "yarn add", "pip install", "pip3 install",
14
+ "nvm use", "volta install", "pnpm add", "uv pip install", "brew install"];
15
+ const GIT_COMMANDS = ["git commit", "git merge", "git rebase", "git checkout", "git switch",
16
+ "git branch", "git push", "git pull", "git stash", "git reset", "git cherry-pick"];
17
+ function truncate(s) {
18
+ return s.length > DATA_SOFT_CAP ? s.slice(0, DATA_SOFT_CAP) + "..." : s;
19
+ }
20
+ function isSensitivePath(path) {
21
+ const lower = path.toLowerCase();
22
+ return SENSITIVE_PATHS.some(p => lower.includes(p));
23
+ }
24
+ function classifyFile(path) {
25
+ if (/\.(test|spec)\.[tj]sx?$/.test(path) || path.includes("__tests__"))
26
+ return "test";
27
+ if (/\.(json|ya?ml|toml|ini|env)$/.test(path) || path.includes("config"))
28
+ return "config";
29
+ if (/\.(md|txt|rst)$/.test(path) || path.includes("docs/"))
30
+ return "docs";
31
+ return "source";
32
+ }
33
+ function extractBashEvents(input) {
34
+ const command = String(input.tool_input.command ?? "");
35
+ const isError = input.tool_output?.isError === true;
36
+ // Error detection (priority 1)
37
+ if (isError) {
38
+ const prefix = command.split(/\s+/).slice(0, 3).join(" ");
39
+ return [{ type: "error_tool", category: "error", data: truncate(`Bash error: ${prefix}`), priority: 1 }];
40
+ }
41
+ // Git operations (priority 2)
42
+ const gitMatch = GIT_COMMANDS.find(gc => command.startsWith(gc));
43
+ if (gitMatch) {
44
+ const commitMsgMatch = command.match(/-m\s+["']([^"']+)["']/);
45
+ const data = commitMsgMatch
46
+ ? `${gitMatch}: ${commitMsgMatch[1]}`
47
+ : gitMatch;
48
+ return [{ type: `git_${gitMatch.split(" ")[1]}`, category: "git", data: truncate(data), priority: 2 }];
49
+ }
50
+ // Env commands (priority 2)
51
+ const envMatch = ENV_COMMANDS.find(ec => command.startsWith(ec));
52
+ if (envMatch) {
53
+ return [{ type: "env_install", category: "env", data: truncate(command), priority: 2 }];
54
+ }
55
+ return [];
56
+ }
57
+ function extractFileEvents(toolName, input) {
58
+ const filePath = String(input.tool_input.file_path ?? input.tool_input.path ?? input.tool_input.pattern ?? "");
59
+ if (!filePath || isSensitivePath(filePath))
60
+ return [];
61
+ const typeMap = {
62
+ Read: "file_read", Edit: "file_edit", Write: "file_write",
63
+ Glob: "file_glob", Grep: "file_grep",
64
+ };
65
+ return [{
66
+ type: typeMap[toolName] ?? "file_access",
67
+ category: "file",
68
+ data: truncate(`${filePath} (${classifyFile(filePath)})`),
69
+ priority: 3,
70
+ }];
71
+ }
72
+ export function extractPostToolEvents(input) {
73
+ const { tool_name } = input;
74
+ // Skip lcm_store to prevent feedback loops
75
+ if (tool_name.includes("lcm_store") || tool_name.includes("lcm__lcm_store"))
76
+ return [];
77
+ // AskUserQuestion — extract Q+A pair (priority 1)
78
+ if (tool_name === "AskUserQuestion") {
79
+ const question = String(input.tool_input.question ?? "");
80
+ const answer = String(input.tool_response ?? "");
81
+ return [{
82
+ type: "decision",
83
+ category: "decision",
84
+ data: truncate(`Q: ${question}\nA: ${answer}`),
85
+ priority: 1,
86
+ }];
87
+ }
88
+ // Plan mode (priority 1)
89
+ if (tool_name === "EnterPlanMode") {
90
+ return [{ type: "plan_enter", category: "plan", data: "Entered plan mode", priority: 1 }];
91
+ }
92
+ if (tool_name === "ExitPlanMode") {
93
+ const response = String(input.tool_response ?? "");
94
+ const status = /approve/i.test(response) ? "approved" : /reject/i.test(response) ? "rejected" : "exited";
95
+ return [{ type: "plan_exit", category: "plan", data: `Plan ${status}`, priority: 1 }];
96
+ }
97
+ // Bash — multiple categories
98
+ if (tool_name === "Bash") {
99
+ return extractBashEvents(input);
100
+ }
101
+ // File operations (priority 3)
102
+ if (["Read", "Edit", "Write", "Glob", "Grep"].includes(tool_name)) {
103
+ return extractFileEvents(tool_name, input);
104
+ }
105
+ // Task operations (priority 2)
106
+ if (tool_name === "TaskCreate" || tool_name === "TaskUpdate") {
107
+ const subject = String(input.tool_input.subject ?? input.tool_input.taskId ?? "");
108
+ const status = String(input.tool_input.status ?? "created");
109
+ return [{
110
+ type: `task_${tool_name === "TaskCreate" ? "create" : "update"}`,
111
+ category: "task",
112
+ data: truncate(`${subject} → ${status}`),
113
+ priority: 2,
114
+ }];
115
+ }
116
+ // Agent/subagent (priority 3)
117
+ if (tool_name === "Agent") {
118
+ const desc = String(input.tool_input.description ?? "");
119
+ return [{ type: "subagent_dispatch", category: "subagent", data: truncate(desc), priority: 3 }];
120
+ }
121
+ // Skill (priority 3)
122
+ if (tool_name === "Skill") {
123
+ const skill = String(input.tool_input.skill ?? "");
124
+ return [{ type: "skill_use", category: "skill", data: skill, priority: 3 }];
125
+ }
126
+ // MCP tools (priority 3) — tool name only, no args
127
+ if (tool_name.startsWith("mcp__")) {
128
+ return [{ type: "mcp_call", category: "mcp", data: tool_name, priority: 3 }];
129
+ }
130
+ // Any other tool with isError flag
131
+ if (input.tool_output?.isError === true) {
132
+ return [{
133
+ type: "error_tool",
134
+ category: "error",
135
+ data: truncate(`${tool_name} error`),
136
+ priority: 1,
137
+ }];
138
+ }
139
+ return [];
140
+ }
141
+ export function extractUserPromptEvents(prompt) {
142
+ const events = [];
143
+ const lower = prompt.toLowerCase();
144
+ // Decision extraction with negative-match guards
145
+ const hasNegative = NEGATIVE_PATTERNS.some(np => lower.includes(np));
146
+ if (!hasNegative) {
147
+ const decisionPatterns = [
148
+ /\b(don'?t|never|always|prefer|use .+ instead)\b/i,
149
+ ];
150
+ for (const pattern of decisionPatterns) {
151
+ if (pattern.test(prompt)) {
152
+ events.push({
153
+ type: "user_decision",
154
+ category: "decision",
155
+ data: truncate(prompt),
156
+ priority: 1,
157
+ });
158
+ break;
159
+ }
160
+ }
161
+ }
162
+ // Role extraction
163
+ const rolePatterns = [
164
+ /\b(i'?m a|act as|i am a|as a|my role)\b/i,
165
+ /\b(senior|junior|staff|lead|principal)\s+(engineer|developer|scientist|designer)\b/i,
166
+ ];
167
+ for (const pattern of rolePatterns) {
168
+ if (pattern.test(prompt)) {
169
+ events.push({
170
+ type: "user_role",
171
+ category: "role",
172
+ data: truncate(prompt),
173
+ priority: 2,
174
+ });
175
+ break;
176
+ }
177
+ }
178
+ // Intent extraction
179
+ const intentMap = [
180
+ [/\b(why|explain|debug|investigate|understand)\b/i, "investigate"],
181
+ [/\b(create|fix|build|implement|add|write)\b/i, "implement"],
182
+ [/\b(review|check|verify|test|validate)\b/i, "review"],
183
+ [/\b(refactor|clean|simplify|optimize)\b/i, "refactor"],
184
+ ];
185
+ for (const [pattern, intent] of intentMap) {
186
+ if (pattern.test(prompt)) {
187
+ events.push({
188
+ type: `intent_${intent}`,
189
+ category: "intent",
190
+ data: intent,
191
+ priority: 3,
192
+ });
193
+ break;
194
+ }
195
+ }
196
+ return events;
197
+ }
198
+ //# sourceMappingURL=extractors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../../../src/hooks/extractors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /** Returns the log path — overridable via LCM_LOG_PATH env var for test isolation. */
2
+ export declare function getLogPath(): string;
3
+ /** Reset circuit breaker — for testing only. */
4
+ export declare function _resetCircuitBreaker(): void;
5
+ /**
6
+ * Three-layer error fence for hook processes.
7
+ * Layer 1: Sidecar DB error_log table (queryable by doctor/stats)
8
+ * Layer 2: Flat file ~/.lossless-claude/logs/events.log
9
+ * Layer 3: Swallow silently — hooks must never crash
10
+ */
11
+ export declare function safeLogError(hook: string, error: unknown, opts: {
12
+ cwd?: string;
13
+ sessionId?: string;
14
+ }): void;
@@ -0,0 +1,56 @@
1
+ // src/hooks/hook-errors.ts
2
+ import { EventsDb } from "./events-db.js";
3
+ import { eventsDbPath } from "../db/events-path.js";
4
+ import { appendFileSync, mkdirSync } from "node:fs";
5
+ import { dirname } from "node:path";
6
+ import { join } from "node:path";
7
+ import { homedir } from "node:os";
8
+ /** Returns the log path — overridable via LCM_LOG_PATH env var for test isolation. */
9
+ export function getLogPath() {
10
+ return process.env.LCM_LOG_PATH ?? join(homedir(), ".lossless-claude", "logs", "events.log");
11
+ }
12
+ let dbCircuitOpen = false;
13
+ /** Reset circuit breaker — for testing only. */
14
+ export function _resetCircuitBreaker() {
15
+ dbCircuitOpen = false;
16
+ }
17
+ /**
18
+ * Three-layer error fence for hook processes.
19
+ * Layer 1: Sidecar DB error_log table (queryable by doctor/stats)
20
+ * Layer 2: Flat file ~/.lossless-claude/logs/events.log
21
+ * Layer 3: Swallow silently — hooks must never crash
22
+ */
23
+ export function safeLogError(hook, error, opts) {
24
+ // Layer 1: Sidecar DB (skip if cwd missing or circuit open)
25
+ if (opts.cwd && !dbCircuitOpen) {
26
+ try {
27
+ const db = new EventsDb(eventsDbPath(opts.cwd));
28
+ try {
29
+ db.logHookError(hook, error, opts.sessionId);
30
+ }
31
+ finally {
32
+ db.close();
33
+ }
34
+ return;
35
+ }
36
+ catch {
37
+ dbCircuitOpen = true; // skip DB on subsequent calls this process
38
+ }
39
+ }
40
+ // Layer 2: Flat file (include cwd for diagnosing DB-skip cases)
41
+ try {
42
+ const logPath = getLogPath();
43
+ mkdirSync(dirname(logPath), { recursive: true });
44
+ appendFileSync(logPath, JSON.stringify({
45
+ ts: new Date().toISOString(),
46
+ hook,
47
+ error: error instanceof Error ? error.message : String(error),
48
+ session_id: opts.sessionId,
49
+ cwd: opts.cwd,
50
+ }) + "\n");
51
+ return;
52
+ }
53
+ catch { /* file failed — fall through */ }
54
+ // Layer 3: Swallow silently — hooks must never crash
55
+ }
56
+ //# sourceMappingURL=hook-errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hook-errors.js","sourceRoot":"","sources":["../../../src/hooks/hook-errors.ts"],"names":[],"mappings":"AAAA,2BAA2B;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,sFAAsF;AACtF,MAAM,UAAU,UAAU;IACxB,OAAO,OAAO,CAAC,GAAG,CAAC,YAAY,IAAI,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;AAC/F,CAAC;AAED,IAAI,aAAa,GAAG,KAAK,CAAC;AAE1B,gDAAgD;AAChD,MAAM,UAAU,oBAAoB;IAClC,aAAa,GAAG,KAAK,CAAC;AACxB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,YAAY,CAC1B,IAAY,EACZ,KAAc,EACd,IAA0C;IAE1C,4DAA4D;IAC5D,IAAI,IAAI,CAAC,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QAC/B,IAAI,CAAC;YACH,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YAChD,IAAI,CAAC;gBACH,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;YAC/C,CAAC;oBAAS,CAAC;gBACT,EAAE,CAAC,KAAK,EAAE,CAAC;YACb,CAAC;YACD,OAAO;QACT,CAAC;QAAC,MAAM,CAAC;YACP,aAAa,GAAG,IAAI,CAAC,CAAC,2CAA2C;QACnE,CAAC;IACH,CAAC;IAED,gEAAgE;IAChE,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,UAAU,EAAE,CAAC;QAC7B,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,cAAc,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC;YACrC,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAC5B,IAAI;YACJ,KAAK,EAAE,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC;YAC7D,UAAU,EAAE,IAAI,CAAC,SAAS;YAC1B,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,GAAG,IAAI,CAAC,CAAC;QACX,OAAO;IACT,CAAC;IAAC,MAAM,CAAC,CAAC,gCAAgC,CAAC,CAAC;IAE5C,qDAAqD;AACvD,CAAC"}
@@ -0,0 +1,4 @@
1
+ export declare function handlePostToolUse(stdin: string): Promise<{
2
+ exitCode: number;
3
+ stdout: string;
4
+ }>;
@@ -0,0 +1,43 @@
1
+ // src/hooks/post-tool.ts
2
+ import { extractPostToolEvents } from "./extractors.js";
3
+ import { EventsDb } from "./events-db.js";
4
+ import { eventsDbPath } from "../db/events-path.js";
5
+ import { firePromoteEventsRequest } from "./session-end.js";
6
+ import { safeLogError } from "./hook-errors.js";
7
+ export async function handlePostToolUse(stdin) {
8
+ let cwd;
9
+ try {
10
+ const input = JSON.parse(stdin);
11
+ const { session_id, tool_name, tool_input, tool_response, tool_output } = input;
12
+ if (!tool_name || !session_id)
13
+ return { exitCode: 0, stdout: "" };
14
+ const events = extractPostToolEvents({ tool_name, tool_input: tool_input ?? {}, tool_response, tool_output });
15
+ if (events.length === 0)
16
+ return { exitCode: 0, stdout: "" };
17
+ cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
18
+ const dbPath = eventsDbPath(cwd);
19
+ const db = new EventsDb(dbPath);
20
+ try {
21
+ for (const event of events) {
22
+ db.insertEvent(session_id, event, "PostToolUse");
23
+ }
24
+ // Tier 1: fire-and-forget daemon promotion for high-priority events.
25
+ // The /promote-events route uses getUnprocessed() which reads processed_at IS NULL,
26
+ // so events already promoted by this call won't be re-promoted by the batch route
27
+ // at session-end. No additional de-duplication guard needed.
28
+ const hasPriority1 = events.some(e => e.priority === 1);
29
+ if (hasPriority1) {
30
+ const port = input.daemon_port ?? 3737;
31
+ firePromoteEventsRequest(port, { cwd });
32
+ }
33
+ }
34
+ finally {
35
+ db.close();
36
+ }
37
+ }
38
+ catch (error) {
39
+ safeLogError("PostToolUse", error, { cwd });
40
+ }
41
+ return { exitCode: 0, stdout: "" };
42
+ }
43
+ //# sourceMappingURL=post-tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"post-tool.js","sourceRoot":"","sources":["../../../src/hooks/post-tool.ts"],"names":[],"mappings":"AAAA,yBAAyB;AACzB,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AACxD,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAC1C,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACpD,OAAO,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAC5D,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAGhD,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,KAAa;IAEb,IAAI,GAAuB,CAAC;IAC5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;QAChC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,aAAa,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC;QAEhF,IAAI,CAAC,SAAS,IAAI,CAAC,UAAU;YAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAElE,MAAM,MAAM,GAAG,qBAAqB,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,IAAI,EAAE,EAAE,aAAa,EAAE,WAAW,EAAE,CAAC,CAAC;QAC9G,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAE5D,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QACnE,MAAM,MAAM,GAAG,YAAY,CAAC,GAAa,CAAC,CAAC;QAC3C,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,MAAM,CAAC,CAAC;QAEhC,IAAI,CAAC;YACH,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;gBAC3B,EAAE,CAAC,WAAW,CAAC,UAAU,EAAE,KAAK,EAAE,aAAa,CAAC,CAAC;YACnD,CAAC;YAED,qEAAqE;YACrE,oFAAoF;YACpF,kFAAkF;YAClF,6DAA6D;YAC7D,MAAM,YAAY,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,CAAC,CAAC,CAAC;YACxD,IAAI,YAAY,EAAE,CAAC;gBACjB,MAAM,IAAI,GAAG,KAAK,CAAC,WAAW,IAAI,IAAI,CAAC;gBACvC,wBAAwB,CAAC,IAAI,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,YAAY,CAAC,aAAa,EAAE,KAAK,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IAC9C,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;AACrC,CAAC"}
@@ -9,8 +9,38 @@ export async function handleSessionStart(stdin, client, port) {
9
9
  return { exitCode: 0, stdout: "" };
10
10
  try {
11
11
  const input = JSON.parse(stdin || "{}");
12
+ // SessionStart scavenge: prune old processed events and trigger promotion for unprocessed ones
13
+ try {
14
+ const { EventsDb } = await import("./events-db.js");
15
+ const { eventsDbPath } = await import("../db/events-path.js");
16
+ const cwd = input.cwd ?? process.env.CLAUDE_PROJECT_DIR ?? process.cwd();
17
+ const eventsDb = new EventsDb(eventsDbPath(cwd));
18
+ try {
19
+ eventsDb.pruneProcessed(7);
20
+ eventsDb.pruneUnprocessed(10_000, 30);
21
+ eventsDb.pruneErrorLog(30);
22
+ const unprocessed = eventsDb.getUnprocessed(1);
23
+ if (unprocessed.length > 0) {
24
+ const { firePromoteEventsRequest } = await import("./session-end.js");
25
+ firePromoteEventsRequest(daemonPort, { cwd });
26
+ }
27
+ }
28
+ finally {
29
+ eventsDb.close();
30
+ }
31
+ }
32
+ catch {
33
+ // Silent fail — scavenge is best-effort
34
+ }
12
35
  const result = await client.post("/restore", input);
13
- return { exitCode: 0, stdout: result.context || "" };
36
+ let stdout = result.context || "";
37
+ if (result.insights && result.insights.length > 0) {
38
+ const insightsBlock = result.insights
39
+ .map((i) => `- ${i.content} (confidence: ${i.confidence})`)
40
+ .join("\n");
41
+ stdout += `\n<learned-insights source="passive-capture">\nRecent learnings from your previous sessions:\n${insightsBlock}\n</learned-insights>`;
42
+ }
43
+ return { exitCode: 0, stdout };
14
44
  }
15
45
  catch {
16
46
  return { exitCode: 0, stdout: "" };
@@ -1 +1 @@
1
- {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/hooks/restore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAa,EAAE,MAAoB,EAAE,IAAa;IACzF,MAAM,UAAU,GAAG,IAAI,IAAI,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAsB,UAAU,EAAE,KAAK,CAAC,CAAC;QACzE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,IAAI,EAAE,EAAE,CAAC;IACvD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"restore.js","sourceRoot":"","sources":["../../../src/hooks/restore.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAElC,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,KAAa,EAAE,MAAoB,EAAE,IAAa;IACzF,MAAM,UAAU,GAAG,IAAI,IAAI,IAAI,CAAC;IAChC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,kBAAkB,EAAE,YAAY,CAAC,CAAC;IACtE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,YAAY,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,WAAW,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC;IAClG,IAAI,CAAC,SAAS;QAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IAEnD,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC;QAExC,+FAA+F;QAC/F,IAAI,CAAC;YACH,MAAM,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;YACpD,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;YAC9D,MAAM,GAAG,GAAG,KAAK,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YACzE,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC;YACjD,IAAI,CAAC;gBACH,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;gBAC3B,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;gBACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;gBAC3B,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;gBAC/C,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;oBAC3B,MAAM,EAAE,wBAAwB,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;oBACtE,wBAAwB,CAAC,UAAU,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;gBAChD,CAAC;YACH,CAAC;oBAAS,CAAC;gBACT,QAAQ,CAAC,KAAK,EAAE,CAAC;YACnB,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,wCAAwC;QAC1C,CAAC;QAED,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,IAAI,CAAiG,UAAU,EAAE,KAAK,CAAC,CAAC;QACpJ,IAAI,MAAM,GAAG,MAAM,CAAC,OAAO,IAAI,EAAE,CAAC;QAElC,IAAI,MAAM,CAAC,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAClD,MAAM,aAAa,GAAG,MAAM,CAAC,QAAQ;iBAClC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,CAAC,OAAO,iBAAiB,CAAC,CAAC,UAAU,GAAG,CAAC;iBAC1D,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,MAAM,IAAI,iGAAiG,aAAa,uBAAuB,CAAC;QAClJ,CAAC;QAED,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,QAAQ,EAAE,CAAC,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;IACrC,CAAC;AACH,CAAC"}
@@ -10,6 +10,9 @@ import type { DaemonClient } from "../daemon/client.js";
10
10
  * and keeps the event loop alive until a response is received).
11
11
  */
12
12
  export declare function fireCompactRequest(port: number, body: Record<string, unknown>): void;
13
+ export declare function firePromoteRequest(port: number, body: Record<string, unknown>): void;
14
+ export declare function firePromoteEventsRequest(port: number, body: Record<string, unknown>): void;
15
+ export declare function fireSessionCompleteRequest(port: number, body: Record<string, unknown>): void;
13
16
  export declare function handleSessionEnd(stdin: string, client: DaemonClient, port?: number): Promise<{
14
17
  exitCode: number;
15
18
  stdout: string;