cogpit-memory 0.1.2 → 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 +23 -8
- package/dist/cli.js +56 -14
- package/dist/index.js +45 -9
- 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
|
|
|
@@ -135,19 +135,34 @@ bun run build
|
|
|
135
135
|
bun run build:npm
|
|
136
136
|
```
|
|
137
137
|
|
|
138
|
-
##
|
|
138
|
+
## Agent Skill
|
|
139
139
|
|
|
140
|
-
cogpit-memory ships with a
|
|
140
|
+
cogpit-memory ships with a skill that teaches AI agents how to use it automatically — layered drill-down, search workflows, and all command options. Works with Claude Code, Cursor, Gemini CLI, GitHub Copilot, and more.
|
|
141
|
+
|
|
142
|
+
### Install via Skills CLI (recommended)
|
|
143
|
+
|
|
144
|
+
Installs globally across all supported agents:
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
npx skills add gentritbiba/cogpit-memory -g -y
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
Browse at [skills.sh](https://skills.sh).
|
|
151
|
+
|
|
152
|
+
### Install via cogpit-memory CLI
|
|
141
153
|
|
|
142
154
|
```bash
|
|
143
|
-
# Install
|
|
155
|
+
# Install globally (all projects)
|
|
156
|
+
npx cogpit-memory install-skill -g
|
|
157
|
+
|
|
158
|
+
# Install into a single project's .claude/skills/
|
|
144
159
|
npx cogpit-memory install-skill
|
|
145
160
|
|
|
146
161
|
# Or specify a project directory
|
|
147
162
|
npx cogpit-memory install-skill --cwd /path/to/project
|
|
148
163
|
```
|
|
149
164
|
|
|
150
|
-
|
|
165
|
+
Once installed, your AI agent will automatically use `cogpit-memory` when it needs to recall past session context or search conversation history.
|
|
151
166
|
|
|
152
167
|
## License
|
|
153
168
|
|
package/dist/cli.js
CHANGED
|
@@ -812,8 +812,8 @@ 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,
|
|
816
|
-
snippet(search_content, 3, '', '', '...',
|
|
815
|
+
SELECT sc.session_id, sc.source_file, sc.location,
|
|
816
|
+
snippet(search_content, 3, '', '', '...', 120) as snippet
|
|
817
817
|
FROM search_content sc
|
|
818
818
|
`;
|
|
819
819
|
const params = [];
|
|
@@ -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) {
|
|
@@ -1980,6 +2014,7 @@ async function listSessions(opts = {}) {
|
|
|
1980
2014
|
if (opts.cwd && meta.cwd !== opts.cwd) continue;
|
|
1981
2015
|
results.push({
|
|
1982
2016
|
sessionId: meta.sessionId,
|
|
2017
|
+
filePath: file.path,
|
|
1983
2018
|
timestamp: meta.timestamp,
|
|
1984
2019
|
model: meta.model,
|
|
1985
2020
|
cwd: meta.cwd,
|
|
@@ -2028,6 +2063,7 @@ async function currentSession(cwd) {
|
|
|
2028
2063
|
]);
|
|
2029
2064
|
return {
|
|
2030
2065
|
sessionId: meta.sessionId,
|
|
2066
|
+
filePath: latest.path,
|
|
2031
2067
|
timestamp: meta.timestamp,
|
|
2032
2068
|
model: meta.model,
|
|
2033
2069
|
cwd: meta.cwd,
|
|
@@ -2091,9 +2127,9 @@ function findSkillContent() {
|
|
|
2091
2127
|
}
|
|
2092
2128
|
throw new Error("Could not find SKILL.md \u2014 try reinstalling cogpit-memory");
|
|
2093
2129
|
}
|
|
2094
|
-
function installSkill(cwd) {
|
|
2095
|
-
const root = cwd ?? process.cwd();
|
|
2096
|
-
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");
|
|
2097
2133
|
(0, import_node_fs4.mkdirSync)(skillDir, { recursive: true });
|
|
2098
2134
|
const content = findSkillContent();
|
|
2099
2135
|
const dest = (0, import_node_path8.join)(skillDir, "SKILL.md");
|
|
@@ -2169,6 +2205,10 @@ function parseArgs(argv) {
|
|
|
2169
2205
|
case "--cwd":
|
|
2170
2206
|
args.cwd = argv[++i];
|
|
2171
2207
|
break;
|
|
2208
|
+
case "-g":
|
|
2209
|
+
case "--global":
|
|
2210
|
+
args.global = true;
|
|
2211
|
+
break;
|
|
2172
2212
|
}
|
|
2173
2213
|
}
|
|
2174
2214
|
break;
|
|
@@ -2231,7 +2271,7 @@ async function main() {
|
|
|
2231
2271
|
}
|
|
2232
2272
|
break;
|
|
2233
2273
|
case "install-skill":
|
|
2234
|
-
result = installSkill(cmd.args.cwd);
|
|
2274
|
+
result = installSkill(cmd.args.cwd, cmd.args.global);
|
|
2235
2275
|
break;
|
|
2236
2276
|
default:
|
|
2237
2277
|
console.error(JSON.stringify({ error: `Unknown command: ${cmd.command}` }));
|
|
@@ -2263,7 +2303,9 @@ Commands:
|
|
|
2263
2303
|
index stats Show index stats
|
|
2264
2304
|
index rebuild Rebuild full index
|
|
2265
2305
|
|
|
2266
|
-
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)
|
|
2267
2309
|
`);
|
|
2268
2310
|
}
|
|
2269
2311
|
var isBunCompiled = false;
|
package/dist/index.js
CHANGED
|
@@ -898,8 +898,8 @@ 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,
|
|
902
|
-
snippet(search_content, 3, '', '', '...',
|
|
901
|
+
SELECT sc.session_id, sc.source_file, sc.location,
|
|
902
|
+
snippet(search_content, 3, '', '', '...', 120) as snippet
|
|
903
903
|
FROM search_content sc
|
|
904
904
|
`;
|
|
905
905
|
const params = [];
|
|
@@ -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) {
|
|
@@ -2071,6 +2105,7 @@ async function listSessions(opts = {}) {
|
|
|
2071
2105
|
if (opts.cwd && meta.cwd !== opts.cwd) continue;
|
|
2072
2106
|
results.push({
|
|
2073
2107
|
sessionId: meta.sessionId,
|
|
2108
|
+
filePath: file.path,
|
|
2074
2109
|
timestamp: meta.timestamp,
|
|
2075
2110
|
model: meta.model,
|
|
2076
2111
|
cwd: meta.cwd,
|
|
@@ -2119,6 +2154,7 @@ async function currentSession(cwd) {
|
|
|
2119
2154
|
]);
|
|
2120
2155
|
return {
|
|
2121
2156
|
sessionId: meta.sessionId,
|
|
2157
|
+
filePath: latest.path,
|
|
2122
2158
|
timestamp: meta.timestamp,
|
|
2123
2159
|
model: meta.model,
|
|
2124
2160
|
cwd: meta.cwd,
|
package/package.json
CHANGED