@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.
Files changed (2) hide show
  1. package/lib/capture.mjs +45 -1
  2. 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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.9.56",
3
+ "version": "0.9.57",
4
4
  "description": "Connect your AI assistant to Cortex — your org's projects, activity, gaps, and directives, scoped to you.",
5
5
  "type": "module",
6
6
  "bin": {