cogpit-memory 0.1.3 → 0.1.4
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 +8 -6
- package/dist/cli.js +53 -13
- package/dist/index.js +42 -8
- package/package.json +1 -1
- package/skill/SKILL.md +1 -0
package/README.md
CHANGED
|
@@ -40,14 +40,14 @@ cogpit-memory search "authentication"
|
|
|
40
40
|
cogpit-memory sessions # Recent sessions (last 7 days)
|
|
41
41
|
cogpit-memory sessions --cwd /path/to/project # Filter by project
|
|
42
42
|
cogpit-memory sessions --current --cwd /path/to/project # Most recent for a project
|
|
43
|
-
cogpit-memory sessions --max-age
|
|
43
|
+
cogpit-memory sessions --max-age 90d --limit 50 # Custom window
|
|
44
44
|
```
|
|
45
45
|
|
|
46
46
|
| Flag | Default | Description |
|
|
47
47
|
|------|---------|-------------|
|
|
48
48
|
| `--cwd` | all | Filter by working directory |
|
|
49
49
|
| `--limit` | `20` | Max results |
|
|
50
|
-
| `--max-age` | `7d` | Time window (`7d`, `12h`, `
|
|
50
|
+
| `--max-age` | `7d` | Time window — any duration (`7d`, `12h`, `90d`, `365d`) |
|
|
51
51
|
| `--current` | — | Most recent session for `--cwd` |
|
|
52
52
|
|
|
53
53
|
### `context` — Layered session drill-down
|
|
@@ -77,11 +77,11 @@ cogpit-memory search "AuthProvider" --case-sensitive # Case-sensitive
|
|
|
77
77
|
| Flag | Default | Description |
|
|
78
78
|
|------|---------|-------------|
|
|
79
79
|
| `--session` | all | Scope to single session |
|
|
80
|
-
| `--max-age` | `5d` | Time window |
|
|
80
|
+
| `--max-age` | `5d` | Time window — any duration (`5d`, `30d`, `365d`) |
|
|
81
81
|
| `--limit` | `20` | Max returned hits |
|
|
82
82
|
| `--case-sensitive` | `false` | Case sensitivity |
|
|
83
83
|
|
|
84
|
-
Each hit includes a `location` string (e.g. `turn/3/assistantMessage`, `agent/a7f3bc2/toolCall/tc1/result`) that maps directly to L2/L3 drill-down commands.
|
|
84
|
+
Each result includes the `cwd` (working directory where the session ran) and an array of hits. Each hit includes a `location` string (e.g. `turn/3/assistantMessage`, `agent/a7f3bc2/toolCall/tc1/result`) that maps directly to L2/L3 drill-down commands.
|
|
85
85
|
|
|
86
86
|
### `index` — Manage the FTS5 search index
|
|
87
87
|
|
|
@@ -151,9 +151,11 @@ Browse at [skills.sh](https://skills.sh).
|
|
|
151
151
|
|
|
152
152
|
### Install via cogpit-memory CLI
|
|
153
153
|
|
|
154
|
-
Installs into a single project's `.claude/skills/`:
|
|
155
|
-
|
|
156
154
|
```bash
|
|
155
|
+
# Install globally (all projects)
|
|
156
|
+
npx cogpit-memory install-skill -g
|
|
157
|
+
|
|
158
|
+
# Install into a single project's .claude/skills/
|
|
157
159
|
npx cogpit-memory install-skill
|
|
158
160
|
|
|
159
161
|
# Or specify a project directory
|
package/dist/cli.js
CHANGED
|
@@ -812,7 +812,7 @@ var SearchIndex = class {
|
|
|
812
812
|
const caseSensitive = opts?.caseSensitive ?? false;
|
|
813
813
|
const ftsQuery = `"${query.replace(/"/g, '""')}"`;
|
|
814
814
|
let sql = `
|
|
815
|
-
SELECT sc.session_id, sc.location,
|
|
815
|
+
SELECT sc.session_id, sc.source_file, sc.location,
|
|
816
816
|
snippet(search_content, 3, '', '', '...', 120) as snippet
|
|
817
817
|
FROM search_content sc
|
|
818
818
|
`;
|
|
@@ -835,6 +835,7 @@ var SearchIndex = class {
|
|
|
835
835
|
const rows = this.db.prepare(sql).all(...params);
|
|
836
836
|
let hits = rows.map((row) => ({
|
|
837
837
|
sessionId: row.session_id,
|
|
838
|
+
filePath: row.source_file,
|
|
838
839
|
location: row.location,
|
|
839
840
|
snippet: row.snippet,
|
|
840
841
|
matchCount: 1
|
|
@@ -1213,20 +1214,21 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
1213
1214
|
});
|
|
1214
1215
|
const grouped = /* @__PURE__ */ new Map();
|
|
1215
1216
|
for (const hit of hits) {
|
|
1216
|
-
let
|
|
1217
|
-
if (!
|
|
1218
|
-
|
|
1219
|
-
grouped.set(hit.sessionId,
|
|
1217
|
+
let entry = grouped.get(hit.sessionId);
|
|
1218
|
+
if (!entry) {
|
|
1219
|
+
entry = { filePath: hit.filePath, hits: [] };
|
|
1220
|
+
grouped.set(hit.sessionId, entry);
|
|
1220
1221
|
}
|
|
1221
|
-
|
|
1222
|
+
entry.hits.push({
|
|
1222
1223
|
location: hit.location,
|
|
1223
1224
|
snippet: hit.snippet,
|
|
1224
1225
|
matchCount: hit.matchCount
|
|
1225
1226
|
});
|
|
1226
1227
|
}
|
|
1227
1228
|
const results = [];
|
|
1228
|
-
for (const [sid,
|
|
1229
|
-
|
|
1229
|
+
for (const [sid, entry] of grouped) {
|
|
1230
|
+
const cwd = await cwdFromFilePath(entry.filePath);
|
|
1231
|
+
results.push({ sessionId: sid, cwd, hits: entry.hits });
|
|
1230
1232
|
}
|
|
1231
1233
|
let totalHits = hits.length;
|
|
1232
1234
|
let sessionsSearched = grouped.size;
|
|
@@ -1252,6 +1254,37 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
1252
1254
|
}
|
|
1253
1255
|
return rawScanSearch(query, opts.sessionId ?? null, maxAgeMs, limit, caseSensitive, depth);
|
|
1254
1256
|
}
|
|
1257
|
+
var CWD_READ_BYTES = 4096;
|
|
1258
|
+
var cwdCache = /* @__PURE__ */ new Map();
|
|
1259
|
+
async function cwdFromFilePath(filePath) {
|
|
1260
|
+
const cached = cwdCache.get(filePath);
|
|
1261
|
+
if (cached !== void 0) return cached;
|
|
1262
|
+
try {
|
|
1263
|
+
const fh = await (0, import_promises2.open)(filePath, "r");
|
|
1264
|
+
try {
|
|
1265
|
+
const buf = Buffer.alloc(CWD_READ_BYTES);
|
|
1266
|
+
const { bytesRead } = await fh.read(buf, 0, CWD_READ_BYTES, 0);
|
|
1267
|
+
const head = buf.subarray(0, bytesRead).toString("utf-8");
|
|
1268
|
+
const lines = head.split("\n", 10);
|
|
1269
|
+
for (const line of lines) {
|
|
1270
|
+
if (!line) continue;
|
|
1271
|
+
try {
|
|
1272
|
+
const obj = JSON.parse(line);
|
|
1273
|
+
if (obj.cwd) {
|
|
1274
|
+
cwdCache.set(filePath, obj.cwd);
|
|
1275
|
+
return obj.cwd;
|
|
1276
|
+
}
|
|
1277
|
+
} catch {
|
|
1278
|
+
}
|
|
1279
|
+
}
|
|
1280
|
+
} finally {
|
|
1281
|
+
await fh.close();
|
|
1282
|
+
}
|
|
1283
|
+
} catch {
|
|
1284
|
+
}
|
|
1285
|
+
cwdCache.set(filePath, "");
|
|
1286
|
+
return "";
|
|
1287
|
+
}
|
|
1255
1288
|
var SNIPPET_WINDOW = 150;
|
|
1256
1289
|
function generateSnippet(text, matchIdx, queryLen) {
|
|
1257
1290
|
if (matchIdx === -1) return text.slice(0, SNIPPET_WINDOW);
|
|
@@ -1450,6 +1483,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, d
|
|
|
1450
1483
|
totalHits += allHits.length;
|
|
1451
1484
|
const sessionResult = {
|
|
1452
1485
|
sessionId: session.sessionId || (0, import_node_path4.basename)(file.path, ".jsonl"),
|
|
1486
|
+
cwd: session.cwd || "",
|
|
1453
1487
|
hits: []
|
|
1454
1488
|
};
|
|
1455
1489
|
for (const hit of allHits) {
|
|
@@ -2093,9 +2127,9 @@ function findSkillContent() {
|
|
|
2093
2127
|
}
|
|
2094
2128
|
throw new Error("Could not find SKILL.md \u2014 try reinstalling cogpit-memory");
|
|
2095
2129
|
}
|
|
2096
|
-
function installSkill(cwd) {
|
|
2097
|
-
const root = cwd ?? process.cwd();
|
|
2098
|
-
const skillDir = (0, import_node_path8.join)(root, "
|
|
2130
|
+
function installSkill(cwd, global) {
|
|
2131
|
+
const root = global ? (0, import_node_path8.join)(process.env.HOME ?? process.env.USERPROFILE ?? "~", ".claude") : (0, import_node_path8.join)(cwd ?? process.cwd(), ".claude");
|
|
2132
|
+
const skillDir = (0, import_node_path8.join)(root, "skills", "cogpit-memory");
|
|
2099
2133
|
(0, import_node_fs4.mkdirSync)(skillDir, { recursive: true });
|
|
2100
2134
|
const content = findSkillContent();
|
|
2101
2135
|
const dest = (0, import_node_path8.join)(skillDir, "SKILL.md");
|
|
@@ -2171,6 +2205,10 @@ function parseArgs(argv) {
|
|
|
2171
2205
|
case "--cwd":
|
|
2172
2206
|
args.cwd = argv[++i];
|
|
2173
2207
|
break;
|
|
2208
|
+
case "-g":
|
|
2209
|
+
case "--global":
|
|
2210
|
+
args.global = true;
|
|
2211
|
+
break;
|
|
2174
2212
|
}
|
|
2175
2213
|
}
|
|
2176
2214
|
break;
|
|
@@ -2233,7 +2271,7 @@ async function main() {
|
|
|
2233
2271
|
}
|
|
2234
2272
|
break;
|
|
2235
2273
|
case "install-skill":
|
|
2236
|
-
result = installSkill(cmd.args.cwd);
|
|
2274
|
+
result = installSkill(cmd.args.cwd, cmd.args.global);
|
|
2237
2275
|
break;
|
|
2238
2276
|
default:
|
|
2239
2277
|
console.error(JSON.stringify({ error: `Unknown command: ${cmd.command}` }));
|
|
@@ -2265,7 +2303,9 @@ Commands:
|
|
|
2265
2303
|
index stats Show index stats
|
|
2266
2304
|
index rebuild Rebuild full index
|
|
2267
2305
|
|
|
2268
|
-
install-skill [
|
|
2306
|
+
install-skill [options] Install Claude Code skill
|
|
2307
|
+
--cwd <path> Target project directory
|
|
2308
|
+
-g, --global Install to ~/.claude/skills/ (all projects)
|
|
2269
2309
|
`);
|
|
2270
2310
|
}
|
|
2271
2311
|
var isBunCompiled = false;
|
package/dist/index.js
CHANGED
|
@@ -898,7 +898,7 @@ var SearchIndex = class {
|
|
|
898
898
|
const caseSensitive = opts?.caseSensitive ?? false;
|
|
899
899
|
const ftsQuery = `"${query.replace(/"/g, '""')}"`;
|
|
900
900
|
let sql = `
|
|
901
|
-
SELECT sc.session_id, sc.location,
|
|
901
|
+
SELECT sc.session_id, sc.source_file, sc.location,
|
|
902
902
|
snippet(search_content, 3, '', '', '...', 120) as snippet
|
|
903
903
|
FROM search_content sc
|
|
904
904
|
`;
|
|
@@ -921,6 +921,7 @@ var SearchIndex = class {
|
|
|
921
921
|
const rows = this.db.prepare(sql).all(...params);
|
|
922
922
|
let hits = rows.map((row) => ({
|
|
923
923
|
sessionId: row.session_id,
|
|
924
|
+
filePath: row.source_file,
|
|
924
925
|
location: row.location,
|
|
925
926
|
snippet: row.snippet,
|
|
926
927
|
matchCount: 1
|
|
@@ -1304,20 +1305,21 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
1304
1305
|
});
|
|
1305
1306
|
const grouped = /* @__PURE__ */ new Map();
|
|
1306
1307
|
for (const hit of hits) {
|
|
1307
|
-
let
|
|
1308
|
-
if (!
|
|
1309
|
-
|
|
1310
|
-
grouped.set(hit.sessionId,
|
|
1308
|
+
let entry = grouped.get(hit.sessionId);
|
|
1309
|
+
if (!entry) {
|
|
1310
|
+
entry = { filePath: hit.filePath, hits: [] };
|
|
1311
|
+
grouped.set(hit.sessionId, entry);
|
|
1311
1312
|
}
|
|
1312
|
-
|
|
1313
|
+
entry.hits.push({
|
|
1313
1314
|
location: hit.location,
|
|
1314
1315
|
snippet: hit.snippet,
|
|
1315
1316
|
matchCount: hit.matchCount
|
|
1316
1317
|
});
|
|
1317
1318
|
}
|
|
1318
1319
|
const results = [];
|
|
1319
|
-
for (const [sid,
|
|
1320
|
-
|
|
1320
|
+
for (const [sid, entry] of grouped) {
|
|
1321
|
+
const cwd = await cwdFromFilePath(entry.filePath);
|
|
1322
|
+
results.push({ sessionId: sid, cwd, hits: entry.hits });
|
|
1321
1323
|
}
|
|
1322
1324
|
let totalHits = hits.length;
|
|
1323
1325
|
let sessionsSearched = grouped.size;
|
|
@@ -1343,6 +1345,37 @@ async function searchSessions(query, opts, searchIndex) {
|
|
|
1343
1345
|
}
|
|
1344
1346
|
return rawScanSearch(query, opts.sessionId ?? null, maxAgeMs, limit, caseSensitive, depth);
|
|
1345
1347
|
}
|
|
1348
|
+
var CWD_READ_BYTES = 4096;
|
|
1349
|
+
var cwdCache = /* @__PURE__ */ new Map();
|
|
1350
|
+
async function cwdFromFilePath(filePath) {
|
|
1351
|
+
const cached = cwdCache.get(filePath);
|
|
1352
|
+
if (cached !== void 0) return cached;
|
|
1353
|
+
try {
|
|
1354
|
+
const fh = await (0, import_promises2.open)(filePath, "r");
|
|
1355
|
+
try {
|
|
1356
|
+
const buf = Buffer.alloc(CWD_READ_BYTES);
|
|
1357
|
+
const { bytesRead } = await fh.read(buf, 0, CWD_READ_BYTES, 0);
|
|
1358
|
+
const head = buf.subarray(0, bytesRead).toString("utf-8");
|
|
1359
|
+
const lines = head.split("\n", 10);
|
|
1360
|
+
for (const line of lines) {
|
|
1361
|
+
if (!line) continue;
|
|
1362
|
+
try {
|
|
1363
|
+
const obj = JSON.parse(line);
|
|
1364
|
+
if (obj.cwd) {
|
|
1365
|
+
cwdCache.set(filePath, obj.cwd);
|
|
1366
|
+
return obj.cwd;
|
|
1367
|
+
}
|
|
1368
|
+
} catch {
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
} finally {
|
|
1372
|
+
await fh.close();
|
|
1373
|
+
}
|
|
1374
|
+
} catch {
|
|
1375
|
+
}
|
|
1376
|
+
cwdCache.set(filePath, "");
|
|
1377
|
+
return "";
|
|
1378
|
+
}
|
|
1346
1379
|
var SNIPPET_WINDOW = 150;
|
|
1347
1380
|
function generateSnippet(text, matchIdx, queryLen) {
|
|
1348
1381
|
if (matchIdx === -1) return text.slice(0, SNIPPET_WINDOW);
|
|
@@ -1541,6 +1574,7 @@ async function rawScanSearch(query, sessionId, maxAgeMs, limit, caseSensitive, d
|
|
|
1541
1574
|
totalHits += allHits.length;
|
|
1542
1575
|
const sessionResult = {
|
|
1543
1576
|
sessionId: session.sessionId || (0, import_node_path4.basename)(file.path, ".jsonl"),
|
|
1577
|
+
cwd: session.cwd || "",
|
|
1544
1578
|
hits: []
|
|
1545
1579
|
};
|
|
1546
1580
|
for (const hit of allHits) {
|
package/package.json
CHANGED