@theronap/cortex-mcp 0.9.28 → 0.9.30
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/bin/cortex-mcp.mjs +3 -7
- package/lib/capture.mjs +3 -8
- package/lib/server.mjs +1 -1
- package/package.json +1 -1
- package/lib/materialize.mjs +0 -117
package/bin/cortex-mcp.mjs
CHANGED
|
@@ -46,7 +46,6 @@ if (cmd === '--help' || cmd === '-h' || cmd === 'help') {
|
|
|
46
46
|
` doctor live health check — confirm your token works (no restart needed)\n` +
|
|
47
47
|
` status one-line connected/not-connected check (used by the SessionStart hook)\n` +
|
|
48
48
|
` skills install/repair the managed Cortex skills (also wired by setup)\n` +
|
|
49
|
-
` materialize [--drain] build brain pages on your Claude subscription (claude -p); --drain clears the backlog\n` +
|
|
50
49
|
` snapshot-context save the exact startup context Cortex served to a local snapshot\n` +
|
|
51
50
|
` capture Stop-hook capturer (invoked by Claude Code)\n` +
|
|
52
51
|
` ingest-folder <path> ingest a local markdown folder as your authored records\n` +
|
|
@@ -98,12 +97,9 @@ if (cmd === 'setup') {
|
|
|
98
97
|
const { closeFetch } = await import('../lib/diagnose.mjs')
|
|
99
98
|
await closeFetch()
|
|
100
99
|
} else if (cmd === 'materialize') {
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
104
|
-
process.exitCode = await runMaterializeCli(rest)
|
|
105
|
-
const { closeFetch } = await import('../lib/diagnose.mjs')
|
|
106
|
-
await closeFetch()
|
|
100
|
+
// EXCISED 2026-07-02 (legacy-materializer incident): the digest pipeline deleted live-authored
|
|
101
|
+
// pages. Pages come from live authoring now; this stub keeps old hook invocations harmless.
|
|
102
|
+
process.stderr.write('cortex-mcp: `materialize` was removed — pages come from live authoring (the `author` tool + /log). Nothing to do.\n')
|
|
107
103
|
} else if (cmd === 'ingest-folder') {
|
|
108
104
|
const { runIngestFolder } = await import('../lib/ingest_folder.mjs')
|
|
109
105
|
await runIngestFolder(rest)
|
package/lib/capture.mjs
CHANGED
|
@@ -5,7 +5,6 @@ import { createHash } from 'crypto'
|
|
|
5
5
|
import { fetchCortex, classify, resolveBase } from './diagnose.mjs'
|
|
6
6
|
import { extractSession } from './edge_extract.mjs'
|
|
7
7
|
import { extractTyped } from './extract_typed.mjs'
|
|
8
|
-
import { runMaterialize } from './materialize.mjs'
|
|
9
8
|
import { redactSecrets } from './redact.mjs'
|
|
10
9
|
|
|
11
10
|
// Fetch the node-type registry (global + this org's custom types) — the catalog the typed extractor
|
|
@@ -166,11 +165,7 @@ export async function runCapture() {
|
|
|
166
165
|
process.stderr.write(`cortex: ingest failed — ${d.message}\n`)
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
// `cortex-mcp materialize --drain` is the bulk/backlog path. Best-effort: never blocks capture.
|
|
173
|
-
if (process.env.CORTEX_MATERIALIZE) {
|
|
174
|
-
try { await runMaterialize({ max: 3 }) } catch { /* best-effort */ }
|
|
175
|
-
}
|
|
168
|
+
// The post-capture edge-materialize batch (CORTEX_MATERIALIZE → runMaterialize) was EXCISED
|
|
169
|
+
// 2026-07-02 with the legacy materializer: its server pipeline deleted live-authored pages whose
|
|
170
|
+
// record-hashes drifted. Pages come from live authoring (the `author` tool + /log sweep) now.
|
|
176
171
|
}
|
package/lib/server.mjs
CHANGED
|
@@ -645,7 +645,7 @@ export async function runServer(version) {
|
|
|
645
645
|
{
|
|
646
646
|
title: 'Authoring context (call before author)',
|
|
647
647
|
description:
|
|
648
|
-
'Fetch the scaffolding to author a Cortex wiki node: the canonical NAMESPACE (existing node names — link to these with the EXACT name inside [[ ]]) and the node-type CONNECTION RULES (what kinds of links to look for). ALWAYS call this BEFORE `author` so the page links to real nodes by their established names instead of minting synonyms. Reference a node in the namespace as [[Name]]; if you reference something real that is NOT in the namespace, still write [[Name]] — that is a red-link marking a node worth creating. If the page IS about a specific code repo, also stamp [[repo:owner/name]] (lowercase)
|
|
648
|
+
'Fetch the scaffolding to author a Cortex wiki node: the canonical NAMESPACE (existing node names — link to these with the EXACT name inside [[ ]]) and the node-type CONNECTION RULES (what kinds of links to look for). ALWAYS call this BEFORE `author` so the page links to real nodes by their established names instead of minting synonyms. Reference a node in the namespace as [[Name]]; if you reference something real that is NOT in the namespace, still write [[Name]] — that is a red-link marking a node worth creating. If the page IS about a specific code repo or chat channel, also stamp it once — [[repo:owner/name]] or [[channel:name]] (lowercase, no #) — identifier join keys, not page links.',
|
|
649
649
|
inputSchema: {
|
|
650
650
|
kind: z.enum(['project', 'person', 'org', 'user']).optional().describe('the node type you are about to author (default project)'),
|
|
651
651
|
},
|
package/package.json
CHANGED
package/lib/materialize.mjs
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'child_process'
|
|
2
|
-
import { fetchCortex, resolveBase } from './diagnose.mjs'
|
|
3
|
-
import { edgeSafeEnv } from './edge_extract.mjs'
|
|
4
|
-
|
|
5
|
-
// Edge materialize worker (keystone fix "b"): the brain-page summarizer moved OFF the dead server-side
|
|
6
|
-
// Anthropic API onto the user's Claude SUBSCRIPTION (`claude -p`), mirroring edge extraction + edge
|
|
7
|
-
// relationship inference. The SERVER still owns gather + tier-authorization + the write
|
|
8
|
-
// (/api/brain/materialize/{claim,submit}); this worker ONLY runs the LLM step. It claims a batch of
|
|
9
|
-
// jobs the user is authorized to summarize, runs `claude --print` per (node, tier), and POSTs the RAW
|
|
10
|
-
// model text back for the server to parse + validate + write. The edge is never trusted: it cannot
|
|
11
|
-
// pick a node, choose a tier, or shape a section — it only turns a server-handed corpus into text.
|
|
12
|
-
//
|
|
13
|
-
// Best-effort throughout: a tier the edge can't summarize is simply not submitted and stays queued for
|
|
14
|
-
// a later run. Guarded by CORTEX_SUMMARIZING so it never recurses through the Stop-hook capturer.
|
|
15
|
-
|
|
16
|
-
const SUMMARY_MODEL = process.env.CORTEX_SUMMARY_MODEL ?? 'claude-haiku-4-5'
|
|
17
|
-
|
|
18
|
-
// ONE `claude --print` call → the raw model text for a single tier's digest prompt. Subscription auth
|
|
19
|
-
// via edgeSafeEnv (keeps CLAUDE_CODE_OAUTH_TOKEN, strips billed/proxy ANTHROPIC_*). null on any failure.
|
|
20
|
-
// The server returns a separate system + user prompt; claude --print takes one prompt, so we prepend
|
|
21
|
-
// the system instructions (same as edge_extract puts its instructions in the prompt body).
|
|
22
|
-
function summarizeTier(tier) {
|
|
23
|
-
const prompt = `${tier.system}\n\n${tier.user}`
|
|
24
|
-
try {
|
|
25
|
-
const r = spawnSync(
|
|
26
|
-
'claude',
|
|
27
|
-
['--print', '--model', SUMMARY_MODEL, prompt],
|
|
28
|
-
{
|
|
29
|
-
env: edgeSafeEnv(process.env, { CORTEX_SUMMARIZING: '1' }),
|
|
30
|
-
encoding: 'utf8',
|
|
31
|
-
timeout: 120_000,
|
|
32
|
-
maxBuffer: 8 * 1024 * 1024,
|
|
33
|
-
},
|
|
34
|
-
)
|
|
35
|
-
if (r.status !== 0 || !r.stdout) return null
|
|
36
|
-
return r.stdout.trim() || null
|
|
37
|
-
} catch {
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
async function claimBatch(base, token, max) {
|
|
43
|
-
const res = await fetchCortex(`${base}/api/brain/materialize/claim`, {
|
|
44
|
-
method: 'POST',
|
|
45
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
46
|
-
body: JSON.stringify({ max }),
|
|
47
|
-
})
|
|
48
|
-
if (!res.ok) throw new Error(`claim HTTP ${res.status}`)
|
|
49
|
-
const j = await res.json().catch(() => ({}))
|
|
50
|
-
return Array.isArray(j.jobs) ? j.jobs : []
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
async function submitNode(base, token, kind, ref, results) {
|
|
54
|
-
const res = await fetchCortex(`${base}/api/brain/materialize/submit`, {
|
|
55
|
-
method: 'POST',
|
|
56
|
-
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
|
|
57
|
-
body: JSON.stringify({ kind, ref, results }),
|
|
58
|
-
})
|
|
59
|
-
if (!res.ok) throw new Error(`submit HTTP ${res.status}`)
|
|
60
|
-
return res.json().catch(() => ({}))
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
// Claim → summarize → submit. opts.drain loops until the queue is empty (clears a backlog); otherwise
|
|
64
|
-
// it does ONE bounded batch (the freshness path called after a capture). Returns a tally.
|
|
65
|
-
export async function runMaterialize(opts = {}) {
|
|
66
|
-
const log = opts.log ?? ((m) => process.stderr.write(`${m}\n`))
|
|
67
|
-
const token = process.env.CORTEX_TOKEN
|
|
68
|
-
if (!token) { log('cortex: CORTEX_TOKEN not set, skipping materialize'); return { built: 0, batches: 0 } }
|
|
69
|
-
// Never run inside the summarizer subprocess (it sets CORTEX_SUMMARIZING) — would recurse.
|
|
70
|
-
if (process.env.CORTEX_SUMMARIZING) return { built: 0, batches: 0 }
|
|
71
|
-
|
|
72
|
-
const base = resolveBase(process.env.CORTEX_URL)
|
|
73
|
-
const max = Math.min(Math.max(Number(opts.max) || 6, 1), 12)
|
|
74
|
-
const maxBatches = opts.drain ? (Number(opts.maxBatches) || 50) : 1
|
|
75
|
-
|
|
76
|
-
let built = 0
|
|
77
|
-
let batches = 0
|
|
78
|
-
for (let i = 0; i < maxBatches; i++) {
|
|
79
|
-
let jobs
|
|
80
|
-
try {
|
|
81
|
-
jobs = await claimBatch(base, token, max)
|
|
82
|
-
} catch (e) {
|
|
83
|
-
log(`cortex: materialize claim failed — ${e.message}`)
|
|
84
|
-
break
|
|
85
|
-
}
|
|
86
|
-
if (!jobs.length) break
|
|
87
|
-
batches++
|
|
88
|
-
for (const job of jobs) {
|
|
89
|
-
const results = []
|
|
90
|
-
for (const tier of job.tiers ?? []) {
|
|
91
|
-
const text = summarizeTier(tier)
|
|
92
|
-
if (text) results.push({ tier: tier.tier, text })
|
|
93
|
-
}
|
|
94
|
-
if (!results.length) { log(`cortex: ${job.kind} "${job.name}" — summarizer produced nothing, leaving queued`); continue }
|
|
95
|
-
try {
|
|
96
|
-
const out = await submitNode(base, token, job.kind, job.ref, results)
|
|
97
|
-
const n = out.built ?? 0
|
|
98
|
-
built += n
|
|
99
|
-
log(`cortex: materialized ${job.kind} "${job.name}" — ${n} tier${n === 1 ? '' : 's'}${out.settled ? '' : ' (partial)'}`)
|
|
100
|
-
} catch (e) {
|
|
101
|
-
log(`cortex: materialize submit failed for ${job.kind} "${job.name}" — ${e.message}`)
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
if (opts.drain) log(`cortex: materialize drain complete — ${built} tier-page(s) built across ${batches} batch(es)`)
|
|
106
|
-
return { built, batches }
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// CLI entry: `cortex-mcp materialize [--drain] [--max N]`. --drain clears the whole backlog now;
|
|
110
|
-
// without it, one bounded batch. Always exits 0 (best-effort maintenance, never a hard failure).
|
|
111
|
-
export async function runMaterializeCli(args = []) {
|
|
112
|
-
const drain = args.includes('--drain')
|
|
113
|
-
const mi = args.indexOf('--max')
|
|
114
|
-
const max = mi >= 0 ? Number(args[mi + 1]) : undefined
|
|
115
|
-
await runMaterialize({ drain, max })
|
|
116
|
-
return 0
|
|
117
|
-
}
|