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 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 30d --limit 50 # Custom window
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`, `30d`) |
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
- ## Claude Code Skill
138
+ ## Agent Skill
139
139
 
140
- cogpit-memory ships with a [Claude Code skill](https://docs.anthropic.com/en/docs/claude-code/skills) that teaches Claude how to use it automatically — layered drill-down, search workflows, and all command options.
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 the skill into the current project
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
- This creates `.claude/skills/cogpit-memory/SKILL.md` in your project. Once installed, Claude Code will automatically use `cogpit-memory` when it needs to recall past session context or search conversation history.
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, '', '', '...', 40) as snippet
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 sessionHits = grouped.get(hit.sessionId);
1217
- if (!sessionHits) {
1218
- sessionHits = [];
1219
- grouped.set(hit.sessionId, sessionHits);
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
- sessionHits.push({
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, sessionHits] of grouped) {
1229
- results.push({ sessionId: sid, hits: sessionHits });
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, ".claude", "skills", "cogpit-memory");
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 [--cwd path] Install Claude Code skill to .claude/skills/
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, '', '', '...', 40) as snippet
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 sessionHits = grouped.get(hit.sessionId);
1308
- if (!sessionHits) {
1309
- sessionHits = [];
1310
- grouped.set(hit.sessionId, sessionHits);
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
- sessionHits.push({
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, sessionHits] of grouped) {
1320
- results.push({ sessionId: sid, hits: sessionHits });
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogpit-memory",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "CLI tool for Claude Code session introspection — search, browse, and drill into past sessions",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./src/index.ts",
package/skill/SKILL.md CHANGED
@@ -262,6 +262,7 @@ bunx cogpit-memory search "AuthProvider" --case-sensitive
262
262
  "results": [
263
263
  {
264
264
  "sessionId": "abc-123",
265
+ "cwd": "/path/to/project",
265
266
  "hits": [
266
267
  {
267
268
  "location": "turn/3/userMessage",