@theronap/cortex-mcp 0.9.56 → 0.9.57
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/lib/capture.mjs +45 -1
- package/package.json +1 -1
package/lib/capture.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { readFileSync } from 'fs'
|
|
2
|
-
import { spawn } from 'child_process'
|
|
2
|
+
import { spawn, execFileSync } from 'child_process'
|
|
3
3
|
import { homedir } from 'os'
|
|
4
4
|
import { resolve, dirname, join } from 'path'
|
|
5
5
|
import { fileURLToPath } from 'url'
|
|
@@ -37,6 +37,45 @@ export function projectFrom(cwd) {
|
|
|
37
37
|
|
|
38
38
|
// Claude Code Stop hook → POSTs a session digest to Cortex cloud, which
|
|
39
39
|
// summarizes server-side and upserts ONE record per session. Node-native
|
|
40
|
+
// SHR-01/T6 — parse a git remote URL into GitHub 'owner/name', or null.
|
|
41
|
+
//
|
|
42
|
+
// Handles the four remote forms git emits: scp-like ssh (git@github.com:o/n.git), ssh://, https://,
|
|
43
|
+
// git://. The HOST CHECK IS LOAD-BEARING, not cosmetic: this string becomes a clearance key, and
|
|
44
|
+
// 'owner/name' on gitlab.com or a self-hosted forge would collide in the identifier namespace with an
|
|
45
|
+
// unrelated GitHub repo of the same name — granting its members read access to each other's sessions.
|
|
46
|
+
export function githubFullName(url) {
|
|
47
|
+
if (!url) return null
|
|
48
|
+
const m = String(url).trim()
|
|
49
|
+
.match(/^(?:git\+)?(?:https?:\/\/|ssh:\/\/|git:\/\/)?(?:[^@/]+@)?github\.com[:/]+([^/]+)\/(.+?)(?:\.git)?\/?$/i)
|
|
50
|
+
if (!m) return null
|
|
51
|
+
const owner = m[1].toLowerCase()
|
|
52
|
+
const name = m[2].toLowerCase()
|
|
53
|
+
if (!owner || !name || name.includes('/')) return null
|
|
54
|
+
return `${owner}/${name}`
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// SHR-01/T6 — the repo this session is working in, as 'owner/name', or null.
|
|
58
|
+
//
|
|
59
|
+
// D2, FAIL CLOSED: anything that is not unambiguously a GitHub worktree — no repo, no origin remote,
|
|
60
|
+
// a non-GitHub host, git not installed — returns null, and the session is then stamped with NO
|
|
61
|
+
// identifier and stays private. There is deliberately no fallback: a brain-level or hostname-level
|
|
62
|
+
// identifier would be held by every member of the org, so overlap would ALWAYS succeed. That is an
|
|
63
|
+
// accidental org-wide grant, i.e. the default-tier flip that was explicitly declined.
|
|
64
|
+
//
|
|
65
|
+
// `git config --get` is local and does no network I/O. Bounded and swallowed regardless: capture must
|
|
66
|
+
// never break a session, and a missing identifier is a private session, not a broken one.
|
|
67
|
+
export function repoFullNameFrom(cwd) {
|
|
68
|
+
if (!cwd) return null
|
|
69
|
+
try {
|
|
70
|
+
const url = execFileSync('git', ['-C', String(cwd), 'config', '--get', 'remote.origin.url'], {
|
|
71
|
+
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
72
|
+
})
|
|
73
|
+
return githubFullName(url)
|
|
74
|
+
} catch {
|
|
75
|
+
return null
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
40
79
|
// (no bun, no repo clone). Always exits 0 — capture must never break a session.
|
|
41
80
|
|
|
42
81
|
function readStdin() {
|
|
@@ -159,6 +198,10 @@ async function captureWork(stdinRaw) {
|
|
|
159
198
|
// summarizer (which has been the silent point of failure). Fall back to shipping the transcript
|
|
160
199
|
// tail only if local extraction is unavailable (e.g. `claude` not on PATH) so we never drop a
|
|
161
200
|
// session. The server re-validates people/entities — the edge is not trusted.
|
|
201
|
+
// SHR-01/T6: the repo identifier is what lets a PEER read this session (see 0096). Omitted entirely
|
|
202
|
+
// when the cwd is not a GitHub worktree — the record still lands, it just stays private (D2).
|
|
203
|
+
const repoFullName = repoFullNameFrom(hook.cwd)
|
|
204
|
+
|
|
162
205
|
const common = {
|
|
163
206
|
source: 'claude-code',
|
|
164
207
|
project: repo,
|
|
@@ -166,6 +209,7 @@ async function captureWork(stdinRaw) {
|
|
|
166
209
|
title: `Worked in ${repo}`,
|
|
167
210
|
payload: { session_id: hook.session_id, cwd: hook.cwd },
|
|
168
211
|
captureSource: 'hook', // T8: fallback writer — never clobbers a cortex-log ('skill') record
|
|
212
|
+
...(repoFullName ? { repoFullName } : {}),
|
|
169
213
|
...(hydratedFrom.length ? { hydratedFrom } : {}),
|
|
170
214
|
}
|
|
171
215
|
const extracted = transcript ? extractSession(transcript) : null
|