@theronap/cortex-mcp 0.4.5 → 0.4.6

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.
@@ -16,7 +16,7 @@
16
16
  * Get your token from the Cortex console → Connect your AI.
17
17
  */
18
18
 
19
- const VERSION = '0.4.5'
19
+ const VERSION = '0.4.6'
20
20
  const cmd = process.argv[2]
21
21
  const rest = process.argv.slice(3)
22
22
 
package/lib/capture.mjs CHANGED
@@ -25,23 +25,42 @@ function readStdin() {
25
25
  }
26
26
 
27
27
  // Claude transcripts are JSONL; pull human-readable text from the tail so the
28
- // server has real content to summarize (not raw tool JSON).
28
+ // server has real content to summarize (not raw tool JSON). ALSO scan the FULL
29
+ // file for lifecycle-verb lines (§4b status recall — a "we're pausing X" said at
30
+ // minute 5 of a 2-hour session must reach the server's status prefilter even
31
+ // though only the tail ships) and append the flagged spans.
32
+ const LIFECYCLE_RE = /\b(paus\w*|resum\w*|shipp?\w*|kill\w*|cancel\w*|on hold|sunset\w*)\b/i
33
+
29
34
  function transcriptTail(path) {
30
35
  let raw = ''
31
36
  try { raw = readFileSync(path, 'utf8') } catch { return '' }
32
- const lines = raw.split('\n').filter(Boolean).slice(-60)
33
- const texts = []
34
- for (const line of lines) {
37
+ const jsonLines = raw.split('\n').filter(Boolean)
38
+ const textOf = (line) => {
35
39
  try {
36
40
  const obj = JSON.parse(line)
37
41
  const content = obj?.message?.content ?? obj?.content
38
- if (typeof content === 'string') texts.push(content)
39
- else if (Array.isArray(content)) {
40
- for (const c of content) if (c?.type === 'text' && c.text) texts.push(c.text)
41
- }
42
+ if (typeof content === 'string') return [content]
43
+ if (Array.isArray(content)) return content.filter((c) => c?.type === 'text' && c.text).map((c) => c.text)
42
44
  } catch { /* skip non-JSON */ }
45
+ return []
46
+ }
47
+
48
+ const texts = jsonLines.slice(-60).flatMap(textOf)
49
+ const tail = texts.join('\n').slice(-6000)
50
+
51
+ // Full-session status-candidate spans (last 12 matching lines, capped).
52
+ const flagged = []
53
+ for (const line of jsonLines) {
54
+ for (const t of textOf(line)) {
55
+ for (const tl of t.split('\n')) {
56
+ if (LIFECYCLE_RE.test(tl)) flagged.push(tl.trim().slice(0, 200))
57
+ }
58
+ }
43
59
  }
44
- return texts.join('\n').slice(-6000)
60
+ const spans = flagged.slice(-12).join('\n').slice(0, 1200)
61
+ return spans && !tail.includes(spans)
62
+ ? `${tail}\n\n[status-candidate lines from the full session]\n${spans}`
63
+ : tail
45
64
  }
46
65
 
47
66
  export async function runCapture() {
package/lib/server.mjs CHANGED
@@ -102,6 +102,34 @@ export async function runServer(version) {
102
102
  },
103
103
  )
104
104
 
105
+ server.registerTool(
106
+ 'who_knows',
107
+ {
108
+ title: 'Who knows about X',
109
+ description: 'Ranked teammates who have visibly worked on a topic, with evidence — derived only from records you are permitted to see.',
110
+ inputSchema: { topic: z.string().describe('the topic/system/skill, e.g. "the embedding pipeline" or "stripe webhooks"') },
111
+ },
112
+ async ({ topic }) => {
113
+ let res
114
+ try {
115
+ res = await fetchCortex(`${BASE}/api/who-knows?q=${encodeURIComponent(topic)}`, {
116
+ headers: { Authorization: `Bearer ${TOKEN}` },
117
+ })
118
+ } catch (e) {
119
+ return { content: [{ type: 'text', text: `Could not look up experts: ${e.message}` }] }
120
+ }
121
+ if (!res.ok) {
122
+ const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
123
+ return { content: [{ type: 'text', text: `Could not look up experts: ${d.message}` }] }
124
+ }
125
+ const { experts } = await res.json()
126
+ if (!experts?.length) return { content: [{ type: 'text', text: `No one visible to you has recorded work about "${topic}".` }] }
127
+ const lines = experts.map((e, i) =>
128
+ `${i + 1}. ${e.name} — ${e.count} visible record${e.count === 1 ? '' : 's'}\n e.g. ${e.evidence.join(' · ')}`)
129
+ return { content: [{ type: 'text', text: `People with visible work on "${topic}":\n${lines.join('\n')}` }] }
130
+ },
131
+ )
132
+
105
133
  server.registerTool(
106
134
  'set_record_privacy',
107
135
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@theronap/cortex-mcp",
3
- "version": "0.4.5",
3
+ "version": "0.4.6",
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": {