engrm 0.4.7 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +42 -1
- package/dist/cli.js +404 -54
- package/dist/hooks/elicitation-result.js +166 -0
- package/dist/hooks/post-tool-use.js +204 -0
- package/dist/hooks/pre-compact.js +262 -4
- package/dist/hooks/sentinel.js +166 -0
- package/dist/hooks/session-start.js +337 -6
- package/dist/hooks/stop.js +200 -3
- package/dist/hooks/user-prompt-submit.js +1387 -0
- package/dist/server.js +1562 -57
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -18,10 +18,10 @@ var __export = (target, all) => {
|
|
|
18
18
|
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
|
-
import { existsSync as
|
|
22
|
-
import { hostname as hostname2, homedir as
|
|
23
|
-
import { dirname as dirname4, join as
|
|
24
|
-
import { createHash as
|
|
21
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync7, statSync } from "fs";
|
|
22
|
+
import { hostname as hostname2, homedir as homedir4, networkInterfaces as networkInterfaces2 } from "os";
|
|
23
|
+
import { dirname as dirname4, join as join7 } from "path";
|
|
24
|
+
import { createHash as createHash3 } from "crypto";
|
|
25
25
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
26
26
|
|
|
27
27
|
// src/config.ts
|
|
@@ -539,6 +539,64 @@ var MIGRATIONS = [
|
|
|
539
539
|
);
|
|
540
540
|
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
541
541
|
`
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
version: 9,
|
|
545
|
+
description: "Add first-class user prompt capture",
|
|
546
|
+
sql: `
|
|
547
|
+
CREATE TABLE IF NOT EXISTS user_prompts (
|
|
548
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
549
|
+
session_id TEXT NOT NULL,
|
|
550
|
+
project_id INTEGER REFERENCES projects(id),
|
|
551
|
+
prompt_number INTEGER NOT NULL,
|
|
552
|
+
prompt TEXT NOT NULL,
|
|
553
|
+
prompt_hash TEXT NOT NULL,
|
|
554
|
+
cwd TEXT,
|
|
555
|
+
user_id TEXT NOT NULL,
|
|
556
|
+
device_id TEXT NOT NULL,
|
|
557
|
+
agent TEXT DEFAULT 'claude-code',
|
|
558
|
+
created_at_epoch INTEGER NOT NULL,
|
|
559
|
+
UNIQUE(session_id, prompt_number)
|
|
560
|
+
);
|
|
561
|
+
|
|
562
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_session
|
|
563
|
+
ON user_prompts(session_id, prompt_number DESC);
|
|
564
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_project
|
|
565
|
+
ON user_prompts(project_id, created_at_epoch DESC);
|
|
566
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_created
|
|
567
|
+
ON user_prompts(created_at_epoch DESC);
|
|
568
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_hash
|
|
569
|
+
ON user_prompts(prompt_hash);
|
|
570
|
+
`
|
|
571
|
+
},
|
|
572
|
+
{
|
|
573
|
+
version: 10,
|
|
574
|
+
description: "Add first-class tool event chronology",
|
|
575
|
+
sql: `
|
|
576
|
+
CREATE TABLE IF NOT EXISTS tool_events (
|
|
577
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
578
|
+
session_id TEXT NOT NULL,
|
|
579
|
+
project_id INTEGER REFERENCES projects(id),
|
|
580
|
+
tool_name TEXT NOT NULL,
|
|
581
|
+
tool_input_json TEXT,
|
|
582
|
+
tool_response_preview TEXT,
|
|
583
|
+
file_path TEXT,
|
|
584
|
+
command TEXT,
|
|
585
|
+
user_id TEXT NOT NULL,
|
|
586
|
+
device_id TEXT NOT NULL,
|
|
587
|
+
agent TEXT DEFAULT 'claude-code',
|
|
588
|
+
created_at_epoch INTEGER NOT NULL
|
|
589
|
+
);
|
|
590
|
+
|
|
591
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_session
|
|
592
|
+
ON tool_events(session_id, created_at_epoch DESC, id DESC);
|
|
593
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_project
|
|
594
|
+
ON tool_events(project_id, created_at_epoch DESC, id DESC);
|
|
595
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_tool_name
|
|
596
|
+
ON tool_events(tool_name, created_at_epoch DESC);
|
|
597
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_created
|
|
598
|
+
ON tool_events(created_at_epoch DESC, id DESC);
|
|
599
|
+
`
|
|
542
600
|
}
|
|
543
601
|
];
|
|
544
602
|
function isVecExtensionLoaded(db) {
|
|
@@ -620,9 +678,14 @@ function ensureObservationTypes(db) {
|
|
|
620
678
|
}
|
|
621
679
|
}
|
|
622
680
|
}
|
|
681
|
+
function getSchemaVersion(db) {
|
|
682
|
+
const result = db.query("PRAGMA user_version").get();
|
|
683
|
+
return result.user_version;
|
|
684
|
+
}
|
|
623
685
|
var LATEST_SCHEMA_VERSION = MIGRATIONS.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
|
|
624
686
|
|
|
625
687
|
// src/storage/sqlite.ts
|
|
688
|
+
import { createHash as createHash2 } from "node:crypto";
|
|
626
689
|
var IS_BUN = typeof globalThis.Bun !== "undefined";
|
|
627
690
|
function openDatabase(dbPath) {
|
|
628
691
|
if (IS_BUN) {
|
|
@@ -886,6 +949,110 @@ class MemDatabase {
|
|
|
886
949
|
const now = Math.floor(Date.now() / 1000);
|
|
887
950
|
this.db.query("UPDATE sessions SET status = 'completed', completed_at_epoch = ? WHERE session_id = ?").run(now, sessionId);
|
|
888
951
|
}
|
|
952
|
+
getSessionById(sessionId) {
|
|
953
|
+
return this.db.query("SELECT * FROM sessions WHERE session_id = ?").get(sessionId) ?? null;
|
|
954
|
+
}
|
|
955
|
+
getRecentSessions(projectId, limit = 10, userId) {
|
|
956
|
+
const visibilityClause = userId ? " AND s.user_id = ?" : "";
|
|
957
|
+
if (projectId !== null) {
|
|
958
|
+
return this.db.query(`SELECT
|
|
959
|
+
s.*,
|
|
960
|
+
p.name AS project_name,
|
|
961
|
+
ss.request AS request,
|
|
962
|
+
ss.completed AS completed,
|
|
963
|
+
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
964
|
+
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
965
|
+
FROM sessions s
|
|
966
|
+
LEFT JOIN projects p ON p.id = s.project_id
|
|
967
|
+
LEFT JOIN session_summaries ss ON ss.session_id = s.session_id
|
|
968
|
+
WHERE s.project_id = ?${visibilityClause}
|
|
969
|
+
ORDER BY COALESCE(s.completed_at_epoch, s.started_at_epoch, 0) DESC, s.id DESC
|
|
970
|
+
LIMIT ?`).all(projectId, ...userId ? [userId] : [], limit);
|
|
971
|
+
}
|
|
972
|
+
return this.db.query(`SELECT
|
|
973
|
+
s.*,
|
|
974
|
+
p.name AS project_name,
|
|
975
|
+
ss.request AS request,
|
|
976
|
+
ss.completed AS completed,
|
|
977
|
+
(SELECT COUNT(*) FROM user_prompts up WHERE up.session_id = s.session_id) AS prompt_count,
|
|
978
|
+
(SELECT COUNT(*) FROM tool_events te WHERE te.session_id = s.session_id) AS tool_event_count
|
|
979
|
+
FROM sessions s
|
|
980
|
+
LEFT JOIN projects p ON p.id = s.project_id
|
|
981
|
+
LEFT JOIN session_summaries ss ON ss.session_id = s.session_id
|
|
982
|
+
WHERE 1 = 1${visibilityClause}
|
|
983
|
+
ORDER BY COALESCE(s.completed_at_epoch, s.started_at_epoch, 0) DESC, s.id DESC
|
|
984
|
+
LIMIT ?`).all(...userId ? [userId] : [], limit);
|
|
985
|
+
}
|
|
986
|
+
insertUserPrompt(input) {
|
|
987
|
+
const createdAt = input.created_at_epoch ?? Math.floor(Date.now() / 1000);
|
|
988
|
+
const normalizedPrompt = input.prompt.trim();
|
|
989
|
+
const promptHash = hashPrompt(normalizedPrompt);
|
|
990
|
+
const latest = this.db.query(`SELECT * FROM user_prompts
|
|
991
|
+
WHERE session_id = ?
|
|
992
|
+
ORDER BY prompt_number DESC
|
|
993
|
+
LIMIT 1`).get(input.session_id);
|
|
994
|
+
if (latest && latest.prompt_hash === promptHash) {
|
|
995
|
+
return latest;
|
|
996
|
+
}
|
|
997
|
+
const promptNumber = (latest?.prompt_number ?? 0) + 1;
|
|
998
|
+
const result = this.db.query(`INSERT INTO user_prompts (
|
|
999
|
+
session_id, project_id, prompt_number, prompt, prompt_hash, cwd,
|
|
1000
|
+
user_id, device_id, agent, created_at_epoch
|
|
1001
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.session_id, input.project_id, promptNumber, normalizedPrompt, promptHash, input.cwd ?? null, input.user_id, input.device_id, input.agent ?? "claude-code", createdAt);
|
|
1002
|
+
return this.getUserPromptById(Number(result.lastInsertRowid));
|
|
1003
|
+
}
|
|
1004
|
+
getUserPromptById(id) {
|
|
1005
|
+
return this.db.query("SELECT * FROM user_prompts WHERE id = ?").get(id) ?? null;
|
|
1006
|
+
}
|
|
1007
|
+
getRecentUserPrompts(projectId, limit = 10, userId) {
|
|
1008
|
+
const visibilityClause = userId ? " AND user_id = ?" : "";
|
|
1009
|
+
if (projectId !== null) {
|
|
1010
|
+
return this.db.query(`SELECT * FROM user_prompts
|
|
1011
|
+
WHERE project_id = ?${visibilityClause}
|
|
1012
|
+
ORDER BY created_at_epoch DESC, prompt_number DESC
|
|
1013
|
+
LIMIT ?`).all(projectId, ...userId ? [userId] : [], limit);
|
|
1014
|
+
}
|
|
1015
|
+
return this.db.query(`SELECT * FROM user_prompts
|
|
1016
|
+
WHERE 1 = 1${visibilityClause}
|
|
1017
|
+
ORDER BY created_at_epoch DESC, prompt_number DESC
|
|
1018
|
+
LIMIT ?`).all(...userId ? [userId] : [], limit);
|
|
1019
|
+
}
|
|
1020
|
+
getSessionUserPrompts(sessionId, limit = 20) {
|
|
1021
|
+
return this.db.query(`SELECT * FROM user_prompts
|
|
1022
|
+
WHERE session_id = ?
|
|
1023
|
+
ORDER BY prompt_number ASC
|
|
1024
|
+
LIMIT ?`).all(sessionId, limit);
|
|
1025
|
+
}
|
|
1026
|
+
insertToolEvent(input) {
|
|
1027
|
+
const createdAt = input.created_at_epoch ?? Math.floor(Date.now() / 1000);
|
|
1028
|
+
const result = this.db.query(`INSERT INTO tool_events (
|
|
1029
|
+
session_id, project_id, tool_name, tool_input_json, tool_response_preview,
|
|
1030
|
+
file_path, command, user_id, device_id, agent, created_at_epoch
|
|
1031
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(input.session_id, input.project_id, input.tool_name, input.tool_input_json ?? null, input.tool_response_preview ?? null, input.file_path ?? null, input.command ?? null, input.user_id, input.device_id, input.agent ?? "claude-code", createdAt);
|
|
1032
|
+
return this.getToolEventById(Number(result.lastInsertRowid));
|
|
1033
|
+
}
|
|
1034
|
+
getToolEventById(id) {
|
|
1035
|
+
return this.db.query("SELECT * FROM tool_events WHERE id = ?").get(id) ?? null;
|
|
1036
|
+
}
|
|
1037
|
+
getSessionToolEvents(sessionId, limit = 20) {
|
|
1038
|
+
return this.db.query(`SELECT * FROM tool_events
|
|
1039
|
+
WHERE session_id = ?
|
|
1040
|
+
ORDER BY created_at_epoch ASC, id ASC
|
|
1041
|
+
LIMIT ?`).all(sessionId, limit);
|
|
1042
|
+
}
|
|
1043
|
+
getRecentToolEvents(projectId, limit = 20, userId) {
|
|
1044
|
+
const visibilityClause = userId ? " AND user_id = ?" : "";
|
|
1045
|
+
if (projectId !== null) {
|
|
1046
|
+
return this.db.query(`SELECT * FROM tool_events
|
|
1047
|
+
WHERE project_id = ?${visibilityClause}
|
|
1048
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
1049
|
+
LIMIT ?`).all(projectId, ...userId ? [userId] : [], limit);
|
|
1050
|
+
}
|
|
1051
|
+
return this.db.query(`SELECT * FROM tool_events
|
|
1052
|
+
WHERE 1 = 1${visibilityClause}
|
|
1053
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
1054
|
+
LIMIT ?`).all(...userId ? [userId] : [], limit);
|
|
1055
|
+
}
|
|
889
1056
|
addToOutbox(recordType, recordId) {
|
|
890
1057
|
const now = Math.floor(Date.now() / 1000);
|
|
891
1058
|
this.db.query(`INSERT INTO sync_outbox (record_type, record_id, created_at_epoch)
|
|
@@ -1056,6 +1223,9 @@ class MemDatabase {
|
|
|
1056
1223
|
this.db.query("INSERT OR REPLACE INTO packs_installed (name, installed_at, observation_count) VALUES (?, ?, ?)").run(name, now, observationCount);
|
|
1057
1224
|
}
|
|
1058
1225
|
}
|
|
1226
|
+
function hashPrompt(prompt) {
|
|
1227
|
+
return createHash2("sha256").update(prompt).digest("hex");
|
|
1228
|
+
}
|
|
1059
1229
|
|
|
1060
1230
|
// src/storage/outbox.ts
|
|
1061
1231
|
function getOutboxStats(db) {
|
|
@@ -1410,6 +1580,64 @@ var MIGRATIONS2 = [
|
|
|
1410
1580
|
);
|
|
1411
1581
|
INSERT INTO observations_fts(observations_fts) VALUES('rebuild');
|
|
1412
1582
|
`
|
|
1583
|
+
},
|
|
1584
|
+
{
|
|
1585
|
+
version: 9,
|
|
1586
|
+
description: "Add first-class user prompt capture",
|
|
1587
|
+
sql: `
|
|
1588
|
+
CREATE TABLE IF NOT EXISTS user_prompts (
|
|
1589
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1590
|
+
session_id TEXT NOT NULL,
|
|
1591
|
+
project_id INTEGER REFERENCES projects(id),
|
|
1592
|
+
prompt_number INTEGER NOT NULL,
|
|
1593
|
+
prompt TEXT NOT NULL,
|
|
1594
|
+
prompt_hash TEXT NOT NULL,
|
|
1595
|
+
cwd TEXT,
|
|
1596
|
+
user_id TEXT NOT NULL,
|
|
1597
|
+
device_id TEXT NOT NULL,
|
|
1598
|
+
agent TEXT DEFAULT 'claude-code',
|
|
1599
|
+
created_at_epoch INTEGER NOT NULL,
|
|
1600
|
+
UNIQUE(session_id, prompt_number)
|
|
1601
|
+
);
|
|
1602
|
+
|
|
1603
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_session
|
|
1604
|
+
ON user_prompts(session_id, prompt_number DESC);
|
|
1605
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_project
|
|
1606
|
+
ON user_prompts(project_id, created_at_epoch DESC);
|
|
1607
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_created
|
|
1608
|
+
ON user_prompts(created_at_epoch DESC);
|
|
1609
|
+
CREATE INDEX IF NOT EXISTS idx_user_prompts_hash
|
|
1610
|
+
ON user_prompts(prompt_hash);
|
|
1611
|
+
`
|
|
1612
|
+
},
|
|
1613
|
+
{
|
|
1614
|
+
version: 10,
|
|
1615
|
+
description: "Add first-class tool event chronology",
|
|
1616
|
+
sql: `
|
|
1617
|
+
CREATE TABLE IF NOT EXISTS tool_events (
|
|
1618
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
1619
|
+
session_id TEXT NOT NULL,
|
|
1620
|
+
project_id INTEGER REFERENCES projects(id),
|
|
1621
|
+
tool_name TEXT NOT NULL,
|
|
1622
|
+
tool_input_json TEXT,
|
|
1623
|
+
tool_response_preview TEXT,
|
|
1624
|
+
file_path TEXT,
|
|
1625
|
+
command TEXT,
|
|
1626
|
+
user_id TEXT NOT NULL,
|
|
1627
|
+
device_id TEXT NOT NULL,
|
|
1628
|
+
agent TEXT DEFAULT 'claude-code',
|
|
1629
|
+
created_at_epoch INTEGER NOT NULL
|
|
1630
|
+
);
|
|
1631
|
+
|
|
1632
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_session
|
|
1633
|
+
ON tool_events(session_id, created_at_epoch DESC, id DESC);
|
|
1634
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_project
|
|
1635
|
+
ON tool_events(project_id, created_at_epoch DESC, id DESC);
|
|
1636
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_tool_name
|
|
1637
|
+
ON tool_events(tool_name, created_at_epoch DESC);
|
|
1638
|
+
CREATE INDEX IF NOT EXISTS idx_tool_events_created
|
|
1639
|
+
ON tool_events(created_at_epoch DESC, id DESC);
|
|
1640
|
+
`
|
|
1413
1641
|
}
|
|
1414
1642
|
];
|
|
1415
1643
|
function isVecExtensionLoaded2(db) {
|
|
@@ -1420,14 +1648,14 @@ function isVecExtensionLoaded2(db) {
|
|
|
1420
1648
|
return false;
|
|
1421
1649
|
}
|
|
1422
1650
|
}
|
|
1423
|
-
function
|
|
1651
|
+
function getSchemaVersion2(db) {
|
|
1424
1652
|
const result = db.query("PRAGMA user_version").get();
|
|
1425
1653
|
return result.user_version;
|
|
1426
1654
|
}
|
|
1427
1655
|
var LATEST_SCHEMA_VERSION2 = MIGRATIONS2.filter((m) => !m.condition).reduce((max, m) => Math.max(max, m.version), 0);
|
|
1428
1656
|
|
|
1429
1657
|
// src/provisioning/provision.ts
|
|
1430
|
-
var DEFAULT_CANDENGO_URL = "https://
|
|
1658
|
+
var DEFAULT_CANDENGO_URL = "https://engrm.dev";
|
|
1431
1659
|
|
|
1432
1660
|
class ProvisionError extends Error {
|
|
1433
1661
|
status;
|
|
@@ -1819,6 +2047,7 @@ function registerHooks() {
|
|
|
1819
2047
|
return [runtime, ...runArg, join2(hooksDir, `${name}${ext}`)].join(" ");
|
|
1820
2048
|
}
|
|
1821
2049
|
const sessionStartCmd = hookCmd("session-start");
|
|
2050
|
+
const userPromptSubmitCmd = hookCmd("user-prompt-submit");
|
|
1822
2051
|
const preCompactCmd = hookCmd("pre-compact");
|
|
1823
2052
|
const preToolUseCmd = hookCmd("sentinel");
|
|
1824
2053
|
const postToolUseCmd = hookCmd("post-tool-use");
|
|
@@ -1827,6 +2056,7 @@ function registerHooks() {
|
|
|
1827
2056
|
const settings = readJsonFile(CLAUDE_SETTINGS);
|
|
1828
2057
|
const hooks = settings["hooks"] ?? {};
|
|
1829
2058
|
hooks["SessionStart"] = replaceEngrmHook(hooks["SessionStart"], { hooks: [{ type: "command", command: sessionStartCmd }] }, "session-start");
|
|
2059
|
+
hooks["UserPromptSubmit"] = replaceEngrmHook(hooks["UserPromptSubmit"], { hooks: [{ type: "command", command: userPromptSubmitCmd }] }, "user-prompt-submit");
|
|
1830
2060
|
hooks["PreCompact"] = replaceEngrmHook(hooks["PreCompact"], { hooks: [{ type: "command", command: preCompactCmd }] }, "pre-compact");
|
|
1831
2061
|
hooks["PreToolUse"] = replaceEngrmHook(hooks["PreToolUse"], {
|
|
1832
2062
|
matcher: "Edit|Write",
|
|
@@ -2813,8 +3043,103 @@ async function installRulePacks(db, config, packNames) {
|
|
|
2813
3043
|
return { installed, skipped };
|
|
2814
3044
|
}
|
|
2815
3045
|
|
|
2816
|
-
// src/
|
|
3046
|
+
// src/tools/capture-status.ts
|
|
3047
|
+
import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
|
|
3048
|
+
import { homedir as homedir3 } from "node:os";
|
|
3049
|
+
import { join as join6 } from "node:path";
|
|
2817
3050
|
var LEGACY_CODEX_SERVER_NAME2 = `candengo-${"mem"}`;
|
|
3051
|
+
function getCaptureStatus(db, input = {}) {
|
|
3052
|
+
const hours = Math.max(1, Math.min(input.lookback_hours ?? 24, 24 * 30));
|
|
3053
|
+
const sinceEpoch = Math.floor(Date.now() / 1000) - hours * 3600;
|
|
3054
|
+
const home = input.home_dir ?? homedir3();
|
|
3055
|
+
const claudeJson = join6(home, ".claude.json");
|
|
3056
|
+
const claudeSettings = join6(home, ".claude", "settings.json");
|
|
3057
|
+
const codexConfig = join6(home, ".codex", "config.toml");
|
|
3058
|
+
const codexHooks = join6(home, ".codex", "hooks.json");
|
|
3059
|
+
const claudeJsonContent = existsSync6(claudeJson) ? readFileSync6(claudeJson, "utf-8") : "";
|
|
3060
|
+
const claudeSettingsContent = existsSync6(claudeSettings) ? readFileSync6(claudeSettings, "utf-8") : "";
|
|
3061
|
+
const codexConfigContent = existsSync6(codexConfig) ? readFileSync6(codexConfig, "utf-8") : "";
|
|
3062
|
+
const codexHooksContent = existsSync6(codexHooks) ? readFileSync6(codexHooks, "utf-8") : "";
|
|
3063
|
+
const claudeMcpRegistered = claudeJsonContent.includes('"engrm"');
|
|
3064
|
+
const claudeHooksRegistered = claudeSettingsContent.includes("engrm") || claudeSettingsContent.includes("session-start") || claudeSettingsContent.includes("user-prompt-submit");
|
|
3065
|
+
const codexMcpRegistered = codexConfigContent.includes("[mcp_servers.engrm]") || codexConfigContent.includes(`[mcp_servers.${LEGACY_CODEX_SERVER_NAME2}]`);
|
|
3066
|
+
const codexHooksRegistered = codexHooksContent.includes('"SessionStart"') && codexHooksContent.includes('"Stop"');
|
|
3067
|
+
let claudeHookCount = 0;
|
|
3068
|
+
if (claudeHooksRegistered) {
|
|
3069
|
+
try {
|
|
3070
|
+
const settings = JSON.parse(claudeSettingsContent);
|
|
3071
|
+
const hooks = settings?.hooks ?? {};
|
|
3072
|
+
for (const entries of Object.values(hooks)) {
|
|
3073
|
+
if (!Array.isArray(entries))
|
|
3074
|
+
continue;
|
|
3075
|
+
for (const entry of entries) {
|
|
3076
|
+
const e = entry;
|
|
3077
|
+
if (e.hooks?.some((h) => h.command?.includes("engrm") || h.command?.includes("session-start") || h.command?.includes("user-prompt-submit") || h.command?.includes("sentinel") || h.command?.includes("post-tool-use") || h.command?.includes("pre-compact") || h.command?.includes("stop") || h.command?.includes("elicitation"))) {
|
|
3078
|
+
claudeHookCount++;
|
|
3079
|
+
}
|
|
3080
|
+
}
|
|
3081
|
+
}
|
|
3082
|
+
} catch {}
|
|
3083
|
+
}
|
|
3084
|
+
const visibilityClause = input.user_id ? " AND user_id = ?" : "";
|
|
3085
|
+
const params = input.user_id ? [sinceEpoch, input.user_id] : [sinceEpoch];
|
|
3086
|
+
const recentUserPrompts = db.db.query(`SELECT COUNT(*) as count FROM user_prompts
|
|
3087
|
+
WHERE created_at_epoch >= ?${visibilityClause}`).get(...params)?.count ?? 0;
|
|
3088
|
+
const recentToolEvents = db.db.query(`SELECT COUNT(*) as count FROM tool_events
|
|
3089
|
+
WHERE created_at_epoch >= ?${visibilityClause}`).get(...params)?.count ?? 0;
|
|
3090
|
+
const recentSessionsWithRawCapture = db.db.query(`SELECT COUNT(*) as count
|
|
3091
|
+
FROM sessions s
|
|
3092
|
+
WHERE COALESCE(s.completed_at_epoch, s.started_at_epoch, 0) >= ?
|
|
3093
|
+
${input.user_id ? "AND s.user_id = ?" : ""}
|
|
3094
|
+
AND (
|
|
3095
|
+
EXISTS (SELECT 1 FROM user_prompts up WHERE up.session_id = s.session_id)
|
|
3096
|
+
OR EXISTS (SELECT 1 FROM tool_events te WHERE te.session_id = s.session_id)
|
|
3097
|
+
)`).get(...params)?.count ?? 0;
|
|
3098
|
+
const latestPromptEpoch = db.db.query(`SELECT created_at_epoch FROM user_prompts
|
|
3099
|
+
WHERE 1 = 1${input.user_id ? " AND user_id = ?" : ""}
|
|
3100
|
+
ORDER BY created_at_epoch DESC, prompt_number DESC
|
|
3101
|
+
LIMIT 1`).get(...input.user_id ? [input.user_id] : [])?.created_at_epoch ?? null;
|
|
3102
|
+
const latestToolEventEpoch = db.db.query(`SELECT created_at_epoch FROM tool_events
|
|
3103
|
+
WHERE 1 = 1${input.user_id ? " AND user_id = ?" : ""}
|
|
3104
|
+
ORDER BY created_at_epoch DESC, id DESC
|
|
3105
|
+
LIMIT 1`).get(...input.user_id ? [input.user_id] : [])?.created_at_epoch ?? null;
|
|
3106
|
+
const schemaVersion = getSchemaVersion(db.db);
|
|
3107
|
+
return {
|
|
3108
|
+
schema_version: schemaVersion,
|
|
3109
|
+
schema_current: schemaVersion >= LATEST_SCHEMA_VERSION,
|
|
3110
|
+
claude_mcp_registered: claudeMcpRegistered,
|
|
3111
|
+
claude_hooks_registered: claudeHooksRegistered,
|
|
3112
|
+
claude_hook_count: claudeHookCount,
|
|
3113
|
+
codex_mcp_registered: codexMcpRegistered,
|
|
3114
|
+
codex_hooks_registered: codexHooksRegistered,
|
|
3115
|
+
recent_user_prompts: recentUserPrompts,
|
|
3116
|
+
recent_tool_events: recentToolEvents,
|
|
3117
|
+
recent_sessions_with_raw_capture: recentSessionsWithRawCapture,
|
|
3118
|
+
latest_prompt_epoch: latestPromptEpoch,
|
|
3119
|
+
latest_tool_event_epoch: latestToolEventEpoch,
|
|
3120
|
+
raw_capture_active: recentUserPrompts > 0 || recentToolEvents > 0 || recentSessionsWithRawCapture > 0
|
|
3121
|
+
};
|
|
3122
|
+
}
|
|
3123
|
+
|
|
3124
|
+
// src/sync/auth.ts
|
|
3125
|
+
var LEGACY_PUBLIC_HOSTS = new Set(["www.candengo.com", "candengo.com"]);
|
|
3126
|
+
function normalizeBaseUrl(url) {
|
|
3127
|
+
const trimmed = url.trim();
|
|
3128
|
+
if (!trimmed)
|
|
3129
|
+
return trimmed;
|
|
3130
|
+
try {
|
|
3131
|
+
const parsed = new URL(trimmed);
|
|
3132
|
+
if (LEGACY_PUBLIC_HOSTS.has(parsed.hostname)) {
|
|
3133
|
+
parsed.hostname = "engrm.dev";
|
|
3134
|
+
}
|
|
3135
|
+
return parsed.toString().replace(/\/$/, "");
|
|
3136
|
+
} catch {
|
|
3137
|
+
return trimmed.replace(/\/$/, "");
|
|
3138
|
+
}
|
|
3139
|
+
}
|
|
3140
|
+
|
|
3141
|
+
// src/cli.ts
|
|
3142
|
+
var LEGACY_CODEX_SERVER_NAME3 = `candengo-${"mem"}`;
|
|
2818
3143
|
var args = process.argv.slice(2);
|
|
2819
3144
|
var command = args[0];
|
|
2820
3145
|
var THIS_DIR = dirname4(fileURLToPath4(import.meta.url));
|
|
@@ -3041,13 +3366,13 @@ function writeConfigFromProvision(baseUrl, result) {
|
|
|
3041
3366
|
console.log(`Database initialised at ${getDbPath()}`);
|
|
3042
3367
|
}
|
|
3043
3368
|
function initFromFile(configPath) {
|
|
3044
|
-
if (!
|
|
3369
|
+
if (!existsSync7(configPath)) {
|
|
3045
3370
|
console.error(`Config file not found: ${configPath}`);
|
|
3046
3371
|
process.exit(1);
|
|
3047
3372
|
}
|
|
3048
3373
|
let parsed;
|
|
3049
3374
|
try {
|
|
3050
|
-
const raw =
|
|
3375
|
+
const raw = readFileSync7(configPath, "utf-8");
|
|
3051
3376
|
parsed = JSON.parse(raw);
|
|
3052
3377
|
} catch {
|
|
3053
3378
|
console.error(`Invalid JSON in ${configPath}`);
|
|
@@ -3134,7 +3459,7 @@ async function initManual() {
|
|
|
3134
3459
|
return;
|
|
3135
3460
|
}
|
|
3136
3461
|
}
|
|
3137
|
-
const candengoUrl = await prompt("
|
|
3462
|
+
const candengoUrl = await prompt("Engrm server URL (e.g. https://engrm.dev): ");
|
|
3138
3463
|
const apiKey = await prompt("API key (cvk_...): ");
|
|
3139
3464
|
const siteId = await prompt("Site ID: ");
|
|
3140
3465
|
const namespace = await prompt("Namespace: ");
|
|
@@ -3227,18 +3552,18 @@ function handleStatus() {
|
|
|
3227
3552
|
console.log(` Plan: ${tierLabels[tier] ?? tier}`);
|
|
3228
3553
|
console.log(`
|
|
3229
3554
|
Integration`);
|
|
3230
|
-
console.log(`
|
|
3555
|
+
console.log(` Server: ${config.candengo_url ? normalizeBaseUrl(config.candengo_url) : "(not set)"}`);
|
|
3231
3556
|
console.log(` Sync: ${config.sync.enabled ? "enabled" : "disabled"}`);
|
|
3232
|
-
const claudeJson =
|
|
3233
|
-
const claudeSettings =
|
|
3234
|
-
const codexConfig =
|
|
3235
|
-
const codexHooks =
|
|
3236
|
-
const mcpRegistered =
|
|
3237
|
-
const settingsContent =
|
|
3238
|
-
const codexContent =
|
|
3239
|
-
const codexHooksContent =
|
|
3240
|
-
const hooksRegistered = settingsContent.includes("engrm") || settingsContent.includes("session-start");
|
|
3241
|
-
const codexRegistered = codexContent.includes("[mcp_servers.engrm]") || codexContent.includes(`[mcp_servers.${
|
|
3557
|
+
const claudeJson = join7(homedir4(), ".claude.json");
|
|
3558
|
+
const claudeSettings = join7(homedir4(), ".claude", "settings.json");
|
|
3559
|
+
const codexConfig = join7(homedir4(), ".codex", "config.toml");
|
|
3560
|
+
const codexHooks = join7(homedir4(), ".codex", "hooks.json");
|
|
3561
|
+
const mcpRegistered = existsSync7(claudeJson) && readFileSync7(claudeJson, "utf-8").includes('"engrm"');
|
|
3562
|
+
const settingsContent = existsSync7(claudeSettings) ? readFileSync7(claudeSettings, "utf-8") : "";
|
|
3563
|
+
const codexContent = existsSync7(codexConfig) ? readFileSync7(codexConfig, "utf-8") : "";
|
|
3564
|
+
const codexHooksContent = existsSync7(codexHooks) ? readFileSync7(codexHooks, "utf-8") : "";
|
|
3565
|
+
const hooksRegistered = settingsContent.includes("engrm") || settingsContent.includes("session-start") || settingsContent.includes("user-prompt-submit");
|
|
3566
|
+
const codexRegistered = codexContent.includes("[mcp_servers.engrm]") || codexContent.includes(`[mcp_servers.${LEGACY_CODEX_SERVER_NAME3}]`);
|
|
3242
3567
|
const codexHooksRegistered = codexHooksContent.includes('"SessionStart"') && codexHooksContent.includes('"Stop"');
|
|
3243
3568
|
let hookCount = 0;
|
|
3244
3569
|
if (hooksRegistered) {
|
|
@@ -3249,7 +3574,7 @@ function handleStatus() {
|
|
|
3249
3574
|
if (Array.isArray(entries)) {
|
|
3250
3575
|
for (const entry of entries) {
|
|
3251
3576
|
const e = entry;
|
|
3252
|
-
if (e.hooks?.some((h) => h.command?.includes("engrm") || h.command?.includes("session-start") || h.command?.includes("sentinel") || h.command?.includes("post-tool-use") || h.command?.includes("pre-compact") || h.command?.includes("stop") || h.command?.includes("elicitation"))) {
|
|
3577
|
+
if (e.hooks?.some((h) => h.command?.includes("engrm") || h.command?.includes("session-start") || h.command?.includes("user-prompt-submit") || h.command?.includes("sentinel") || h.command?.includes("post-tool-use") || h.command?.includes("pre-compact") || h.command?.includes("stop") || h.command?.includes("elicitation"))) {
|
|
3253
3578
|
hookCount++;
|
|
3254
3579
|
}
|
|
3255
3580
|
}
|
|
@@ -3269,7 +3594,7 @@ function handleStatus() {
|
|
|
3269
3594
|
if (config.sentinel.provider) {
|
|
3270
3595
|
console.log(` Provider: ${config.sentinel.provider}${config.sentinel.model ? ` (${config.sentinel.model})` : ""}`);
|
|
3271
3596
|
}
|
|
3272
|
-
if (
|
|
3597
|
+
if (existsSync7(getDbPath())) {
|
|
3273
3598
|
try {
|
|
3274
3599
|
const db = new MemDatabase(getDbPath());
|
|
3275
3600
|
const todayStart = Math.floor(new Date().setHours(0, 0, 0, 0) / 1000);
|
|
@@ -3282,7 +3607,7 @@ function handleStatus() {
|
|
|
3282
3607
|
console.log(`
|
|
3283
3608
|
Sentinel: disabled`);
|
|
3284
3609
|
}
|
|
3285
|
-
if (
|
|
3610
|
+
if (existsSync7(getDbPath())) {
|
|
3286
3611
|
try {
|
|
3287
3612
|
const db = new MemDatabase(getDbPath());
|
|
3288
3613
|
const obsCount = db.getActiveObservationCount();
|
|
@@ -3301,6 +3626,10 @@ function handleStatus() {
|
|
|
3301
3626
|
} catch {}
|
|
3302
3627
|
const summaryCount = db.db.query("SELECT COUNT(*) as count FROM session_summaries").get()?.count ?? 0;
|
|
3303
3628
|
console.log(` Sessions: ${summaryCount} summarised`);
|
|
3629
|
+
const capture = getCaptureStatus(db, { user_id: config.user_id });
|
|
3630
|
+
console.log(` Raw capture: ${capture.raw_capture_active ? "active" : "observations-only so far"}`);
|
|
3631
|
+
console.log(` Prompts/tools: ${capture.recent_user_prompts}/${capture.recent_tool_events} in last 24h`);
|
|
3632
|
+
console.log(` Hook state: Claude ${capture.claude_hooks_registered ? "ok" : "missing"}, Codex ${capture.codex_hooks_registered ? "ok" : "missing"}`);
|
|
3304
3633
|
try {
|
|
3305
3634
|
const activeObservations = db.db.query(`SELECT * FROM observations
|
|
3306
3635
|
WHERE lifecycle IN ('active', 'aging', 'pinned') AND superseded_by IS NULL`).all();
|
|
@@ -3374,8 +3703,8 @@ function handleStatus() {
|
|
|
3374
3703
|
Files`);
|
|
3375
3704
|
console.log(` Config: ${getSettingsPath()}`);
|
|
3376
3705
|
console.log(` Database: ${getDbPath()}`);
|
|
3377
|
-
console.log(` Codex config: ${
|
|
3378
|
-
console.log(` Codex hooks: ${
|
|
3706
|
+
console.log(` Codex config: ${join7(homedir4(), ".codex", "config.toml")}`);
|
|
3707
|
+
console.log(` Codex hooks: ${join7(homedir4(), ".codex", "hooks.json")}`);
|
|
3379
3708
|
}
|
|
3380
3709
|
function formatTimeAgo(epoch) {
|
|
3381
3710
|
const ago = Math.floor(Date.now() / 1000) - epoch;
|
|
@@ -3397,7 +3726,7 @@ function formatSyncTime(epochStr) {
|
|
|
3397
3726
|
}
|
|
3398
3727
|
function ensureConfigDir() {
|
|
3399
3728
|
const dir = getConfigDir();
|
|
3400
|
-
if (!
|
|
3729
|
+
if (!existsSync7(dir)) {
|
|
3401
3730
|
mkdirSync3(dir, { recursive: true });
|
|
3402
3731
|
}
|
|
3403
3732
|
}
|
|
@@ -3418,7 +3747,7 @@ function generateDeviceId2() {
|
|
|
3418
3747
|
break;
|
|
3419
3748
|
}
|
|
3420
3749
|
const material = `${host}:${mac || "no-mac"}`;
|
|
3421
|
-
const suffix =
|
|
3750
|
+
const suffix = createHash3("sha256").update(material).digest("hex").slice(0, 8);
|
|
3422
3751
|
return `${host}-${suffix}`;
|
|
3423
3752
|
}
|
|
3424
3753
|
async function handleInstallPack(flags) {
|
|
@@ -3552,7 +3881,7 @@ async function handleDoctor() {
|
|
|
3552
3881
|
return;
|
|
3553
3882
|
}
|
|
3554
3883
|
try {
|
|
3555
|
-
const currentVersion =
|
|
3884
|
+
const currentVersion = getSchemaVersion2(db.db);
|
|
3556
3885
|
if (currentVersion >= LATEST_SCHEMA_VERSION2) {
|
|
3557
3886
|
pass(`Database schema is current (v${currentVersion})`);
|
|
3558
3887
|
} else {
|
|
@@ -3561,10 +3890,10 @@ async function handleDoctor() {
|
|
|
3561
3890
|
} catch {
|
|
3562
3891
|
warn("Could not check database schema version");
|
|
3563
3892
|
}
|
|
3564
|
-
const claudeJson =
|
|
3893
|
+
const claudeJson = join7(homedir4(), ".claude.json");
|
|
3565
3894
|
try {
|
|
3566
|
-
if (
|
|
3567
|
-
const content =
|
|
3895
|
+
if (existsSync7(claudeJson)) {
|
|
3896
|
+
const content = readFileSync7(claudeJson, "utf-8");
|
|
3568
3897
|
if (content.includes('"engrm"')) {
|
|
3569
3898
|
pass("MCP server registered in Claude Code");
|
|
3570
3899
|
} else {
|
|
@@ -3576,10 +3905,10 @@ async function handleDoctor() {
|
|
|
3576
3905
|
} catch {
|
|
3577
3906
|
warn("Could not check MCP server registration");
|
|
3578
3907
|
}
|
|
3579
|
-
const claudeSettings =
|
|
3908
|
+
const claudeSettings = join7(homedir4(), ".claude", "settings.json");
|
|
3580
3909
|
try {
|
|
3581
|
-
if (
|
|
3582
|
-
const content =
|
|
3910
|
+
if (existsSync7(claudeSettings)) {
|
|
3911
|
+
const content = readFileSync7(claudeSettings, "utf-8");
|
|
3583
3912
|
let hookCount = 0;
|
|
3584
3913
|
try {
|
|
3585
3914
|
const settings = JSON.parse(content);
|
|
@@ -3588,7 +3917,7 @@ async function handleDoctor() {
|
|
|
3588
3917
|
if (Array.isArray(entries)) {
|
|
3589
3918
|
for (const entry of entries) {
|
|
3590
3919
|
const e = entry;
|
|
3591
|
-
if (e.hooks?.some((h) => h.command?.includes("engrm") || h.command?.includes("session-start") || h.command?.includes("sentinel") || h.command?.includes("post-tool-use") || h.command?.includes("pre-compact") || h.command?.includes("stop") || h.command?.includes("elicitation"))) {
|
|
3920
|
+
if (e.hooks?.some((h) => h.command?.includes("engrm") || h.command?.includes("session-start") || h.command?.includes("user-prompt-submit") || h.command?.includes("sentinel") || h.command?.includes("post-tool-use") || h.command?.includes("pre-compact") || h.command?.includes("stop") || h.command?.includes("elicitation"))) {
|
|
3592
3921
|
hookCount++;
|
|
3593
3922
|
}
|
|
3594
3923
|
}
|
|
@@ -3606,11 +3935,11 @@ async function handleDoctor() {
|
|
|
3606
3935
|
} catch {
|
|
3607
3936
|
warn("Could not check hooks registration");
|
|
3608
3937
|
}
|
|
3609
|
-
const codexConfig =
|
|
3938
|
+
const codexConfig = join7(homedir4(), ".codex", "config.toml");
|
|
3610
3939
|
try {
|
|
3611
|
-
if (
|
|
3612
|
-
const content =
|
|
3613
|
-
if (content.includes("[mcp_servers.engrm]") || content.includes(`[mcp_servers.${
|
|
3940
|
+
if (existsSync7(codexConfig)) {
|
|
3941
|
+
const content = readFileSync7(codexConfig, "utf-8");
|
|
3942
|
+
if (content.includes("[mcp_servers.engrm]") || content.includes(`[mcp_servers.${LEGACY_CODEX_SERVER_NAME3}]`)) {
|
|
3614
3943
|
pass("MCP server registered in Codex");
|
|
3615
3944
|
} else {
|
|
3616
3945
|
warn("MCP server not registered in Codex");
|
|
@@ -3621,10 +3950,10 @@ async function handleDoctor() {
|
|
|
3621
3950
|
} catch {
|
|
3622
3951
|
warn("Could not check Codex MCP registration");
|
|
3623
3952
|
}
|
|
3624
|
-
const codexHooks =
|
|
3953
|
+
const codexHooks = join7(homedir4(), ".codex", "hooks.json");
|
|
3625
3954
|
try {
|
|
3626
|
-
if (
|
|
3627
|
-
const content =
|
|
3955
|
+
if (existsSync7(codexHooks)) {
|
|
3956
|
+
const content = readFileSync7(codexHooks, "utf-8");
|
|
3628
3957
|
if (content.includes('"SessionStart"') && content.includes('"Stop"')) {
|
|
3629
3958
|
pass("Hooks registered in Codex");
|
|
3630
3959
|
} else {
|
|
@@ -3638,14 +3967,23 @@ async function handleDoctor() {
|
|
|
3638
3967
|
}
|
|
3639
3968
|
if (config.candengo_url) {
|
|
3640
3969
|
try {
|
|
3970
|
+
const baseUrl = normalizeBaseUrl(config.candengo_url);
|
|
3641
3971
|
const controller = new AbortController;
|
|
3642
3972
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
3643
3973
|
const start = Date.now();
|
|
3644
|
-
|
|
3974
|
+
let res = await fetch(`${baseUrl}/health`, { signal: controller.signal });
|
|
3975
|
+
if (res.status === 404) {
|
|
3976
|
+
res = await fetch(`${baseUrl}/v1/mem/provision`, {
|
|
3977
|
+
method: "POST",
|
|
3978
|
+
headers: { "Content-Type": "application/json" },
|
|
3979
|
+
body: "{}",
|
|
3980
|
+
signal: controller.signal
|
|
3981
|
+
});
|
|
3982
|
+
}
|
|
3645
3983
|
clearTimeout(timeout);
|
|
3646
3984
|
const elapsed = Date.now() - start;
|
|
3647
|
-
if (res.ok) {
|
|
3648
|
-
const host = new URL(
|
|
3985
|
+
if (res.ok || res.status === 400) {
|
|
3986
|
+
const host = new URL(baseUrl).hostname;
|
|
3649
3987
|
pass(`Server connectivity (${host}, ${elapsed}ms)`);
|
|
3650
3988
|
} else {
|
|
3651
3989
|
fail(`Server returned HTTP ${res.status}`);
|
|
@@ -3659,16 +3997,16 @@ async function handleDoctor() {
|
|
|
3659
3997
|
}
|
|
3660
3998
|
if (config.candengo_url && config.candengo_api_key) {
|
|
3661
3999
|
try {
|
|
4000
|
+
const baseUrl = normalizeBaseUrl(config.candengo_url);
|
|
3662
4001
|
const controller = new AbortController;
|
|
3663
4002
|
const timeout = setTimeout(() => controller.abort(), 5000);
|
|
3664
|
-
const res = await fetch(`${
|
|
4003
|
+
const res = await fetch(`${baseUrl}/v1/mem/user-settings`, {
|
|
3665
4004
|
headers: { Authorization: `Bearer ${config.candengo_api_key}` },
|
|
3666
4005
|
signal: controller.signal
|
|
3667
4006
|
});
|
|
3668
4007
|
clearTimeout(timeout);
|
|
3669
4008
|
if (res.ok) {
|
|
3670
|
-
const
|
|
3671
|
-
const email = data.email ?? config.user_email ?? "unknown";
|
|
4009
|
+
const email = config.user_email ?? "configured";
|
|
3672
4010
|
pass(`Authentication valid (${email})`);
|
|
3673
4011
|
} else if (res.status === 401 || res.status === 403) {
|
|
3674
4012
|
fail("Authentication failed \u2014 API key may be expired");
|
|
@@ -3717,9 +4055,21 @@ async function handleDoctor() {
|
|
|
3717
4055
|
} catch {
|
|
3718
4056
|
warn("Could not count observations");
|
|
3719
4057
|
}
|
|
4058
|
+
try {
|
|
4059
|
+
const capture = getCaptureStatus(db, { user_id: config.user_id });
|
|
4060
|
+
if (capture.raw_capture_active) {
|
|
4061
|
+
pass(`Raw chronology active (${capture.recent_user_prompts} prompts, ${capture.recent_tool_events} tools in last 24h)`);
|
|
4062
|
+
} else if (capture.claude_hooks_registered || capture.codex_hooks_registered) {
|
|
4063
|
+
warn("Hooks are registered, but no raw prompt/tool chronology has been captured in the last 24h");
|
|
4064
|
+
} else {
|
|
4065
|
+
warn("Raw chronology inactive \u2014 hook registration is incomplete");
|
|
4066
|
+
}
|
|
4067
|
+
} catch {
|
|
4068
|
+
warn("Could not check raw chronology capture");
|
|
4069
|
+
}
|
|
3720
4070
|
try {
|
|
3721
4071
|
const dbPath = getDbPath();
|
|
3722
|
-
if (
|
|
4072
|
+
if (existsSync7(dbPath)) {
|
|
3723
4073
|
const stats = statSync(dbPath);
|
|
3724
4074
|
const sizeMB = stats.size / (1024 * 1024);
|
|
3725
4075
|
const sizeStr = sizeMB >= 1 ? `${sizeMB.toFixed(1)} MB` : `${(stats.size / 1024).toFixed(0)} KB`;
|
|
@@ -3787,11 +4137,11 @@ Registering with Claude Code and Codex...`);
|
|
|
3787
4137
|
console.log(`
|
|
3788
4138
|
Engrm is ready! Start a new Claude Code or Codex session to use memory.`);
|
|
3789
4139
|
} catch (error) {
|
|
3790
|
-
const packageRoot =
|
|
4140
|
+
const packageRoot = join7(THIS_DIR, "..");
|
|
3791
4141
|
const runtime = IS_BUILT_DIST ? process.execPath : "bun";
|
|
3792
|
-
const serverArgs = IS_BUILT_DIST ? [
|
|
3793
|
-
const sessionStartCommand = IS_BUILT_DIST ? `${process.execPath} ${
|
|
3794
|
-
const codexStopCommand = IS_BUILT_DIST ? `${process.execPath} ${
|
|
4142
|
+
const serverArgs = IS_BUILT_DIST ? [join7(packageRoot, "dist", "server.js")] : ["run", join7(packageRoot, "src", "server.ts")];
|
|
4143
|
+
const sessionStartCommand = IS_BUILT_DIST ? `${process.execPath} ${join7(packageRoot, "dist", "hooks", "session-start.js")}` : `bun run ${join7(packageRoot, "hooks", "session-start.ts")}`;
|
|
4144
|
+
const codexStopCommand = IS_BUILT_DIST ? `${process.execPath} ${join7(packageRoot, "dist", "hooks", "codex-stop.js")}` : `bun run ${join7(packageRoot, "hooks", "codex-stop.ts")}`;
|
|
3795
4145
|
console.log(`
|
|
3796
4146
|
Could not auto-register with Claude Code and Codex.`);
|
|
3797
4147
|
console.log(`Error: ${error instanceof Error ? error.message : String(error)}`);
|