@theronap/agnoclast-mcp 0.9.244 → 0.9.246
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 +6 -2
- package/lib/code_graph_cli.mjs +2 -2
- package/lib/edge_extract.mjs +1 -1
- package/lib/extract_typed.mjs +1 -1
- package/lib/graphify_sync.mjs +2 -2
- package/lib/login.mjs +1 -1
- package/lib/obligation_review.mjs +1 -0
- package/lib/obligations_worker.mjs +1 -0
- package/lib/server.mjs +47 -0
- package/lib/uninstall.mjs +5 -5
- package/lib/working_files.mjs +1 -1
- package/package.json +1 -1
package/lib/capture.mjs
CHANGED
|
@@ -72,7 +72,7 @@ export function repoFullNameFrom(cwd) {
|
|
|
72
72
|
if (!cwd) return null
|
|
73
73
|
try {
|
|
74
74
|
const url = execFileSync('git', ['-C', String(cwd), 'config', '--get', 'remote.origin.url'], {
|
|
75
|
-
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
75
|
+
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true,
|
|
76
76
|
})
|
|
77
77
|
return githubFullName(url)
|
|
78
78
|
} catch {
|
|
@@ -108,7 +108,7 @@ export function workingFilesFrom(cwd, max = 40) {
|
|
|
108
108
|
const run = (args) => {
|
|
109
109
|
try {
|
|
110
110
|
return execFileSync('git', ['-C', String(cwd), ...args], {
|
|
111
|
-
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
111
|
+
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true,
|
|
112
112
|
}).split('\n').map((l) => l.trim()).filter(Boolean)
|
|
113
113
|
} catch {
|
|
114
114
|
return []
|
|
@@ -316,12 +316,16 @@ function captureLogFd() {
|
|
|
316
316
|
// feed it the hook payload on its stdin. Returns true if the child was launched (parent may return
|
|
317
317
|
// immediately), false if spawning failed (caller then does the work in-band). The child sees
|
|
318
318
|
// CORTEX_CAPTURE_DETACHED=1 so it runs captureWork() directly instead of forking again.
|
|
319
|
+
// ⚠ WINDOWS: `detached` there means DETACHED_PROCESS — this worker has NO console, so every console
|
|
320
|
+
// program it starts (`claude --print`, `git`) would get a brand-new, visible Windows Terminal window
|
|
321
|
+
// titled with its path. Every spawn in this package passes windowsHide (windows_hide.test.mjs).
|
|
319
322
|
function spawnDetachedWorker(stdinRaw) {
|
|
320
323
|
try {
|
|
321
324
|
const bin = join(dirname(fileURLToPath(import.meta.url)), '..', 'bin', 'cortex-mcp.mjs')
|
|
322
325
|
const child = spawn(process.execPath, [bin, 'capture'], {
|
|
323
326
|
env: { ...process.env, CORTEX_CAPTURE_DETACHED: '1' },
|
|
324
327
|
detached: true,
|
|
328
|
+
windowsHide: true,
|
|
325
329
|
stdio: ['pipe', captureLogFd(), captureLogFd()],
|
|
326
330
|
})
|
|
327
331
|
child.on('error', () => {}) // never let an async spawn error crash the hook
|
package/lib/code_graph_cli.mjs
CHANGED
|
@@ -14,7 +14,7 @@ function graphPath(cwd) {
|
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
function hasGraphifyBinary() {
|
|
17
|
-
const r = spawnSync('graphify', ['--version'], { encoding: 'utf8', timeout: 10_000 })
|
|
17
|
+
const r = spawnSync('graphify', ['--version'], { encoding: 'utf8', timeout: 10_000, windowsHide: true })
|
|
18
18
|
return !r.error && r.status === 0
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -50,7 +50,7 @@ export function runCodeGraphQuery({ action, question, from, to, node }, cwd = pr
|
|
|
50
50
|
const built = buildArgs({ action, question, from, to, node })
|
|
51
51
|
if (built.error) return { ok: false, text: built.error }
|
|
52
52
|
|
|
53
|
-
const r = spawnSync('graphify', built.args, { cwd, encoding: 'utf8', timeout: 60_000, maxBuffer: 4 * 1024 * 1024 })
|
|
53
|
+
const r = spawnSync('graphify', built.args, { cwd, encoding: 'utf8', timeout: 60_000, maxBuffer: 4 * 1024 * 1024, windowsHide: true })
|
|
54
54
|
if (r.error) return { ok: false, text: `graphify failed to run: ${r.error.message}` }
|
|
55
55
|
const out = (r.stdout || '').trim()
|
|
56
56
|
const err = (r.stderr || '').trim()
|
package/lib/edge_extract.mjs
CHANGED
|
@@ -496,7 +496,7 @@ export function extractSession(transcript, opts = {}) {
|
|
|
496
496
|
const r = spawnSync(
|
|
497
497
|
'claude',
|
|
498
498
|
['--print', '--model', process.env.CORTEX_SUMMARY_MODEL ?? 'claude-haiku-4-5'],
|
|
499
|
-
{ input: prompt, env: edgeSafeEnv(process.env, { CORTEX_SUMMARIZING: '1' }), encoding: 'utf8', timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024 },
|
|
499
|
+
{ input: prompt, env: edgeSafeEnv(process.env, { CORTEX_SUMMARIZING: '1' }), encoding: 'utf8', timeout: timeoutMs, maxBuffer: 4 * 1024 * 1024, windowsHide: true },
|
|
500
500
|
)
|
|
501
501
|
const why = classifyEdgeFailure(r, timeoutMs)
|
|
502
502
|
if (why) return edgeSkip(why.reason, why.detail)
|
package/lib/extract_typed.mjs
CHANGED
|
@@ -42,7 +42,7 @@ export function extractTyped(transcript, registry, opts = {}) {
|
|
|
42
42
|
const r = spawnSync(
|
|
43
43
|
'claude',
|
|
44
44
|
['--print', '--model', opts.model ?? process.env.CORTEX_SUMMARY_MODEL ?? 'claude-haiku-4-5', prompt],
|
|
45
|
-
{ env: edgeSafeEnv(process.env, { CORTEX_SUMMARIZING: '1' }), encoding: 'utf8', timeout: 120_000, maxBuffer: 8 * 1024 * 1024 },
|
|
45
|
+
{ env: edgeSafeEnv(process.env, { CORTEX_SUMMARIZING: '1' }), encoding: 'utf8', timeout: 120_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true },
|
|
46
46
|
)
|
|
47
47
|
if (r.status !== 0 || !r.stdout) return null
|
|
48
48
|
return parseTyped(r.stdout)
|
package/lib/graphify_sync.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { fetchCortex, classify, resolveBase, resolveEnvToken } from './diagnose.
|
|
|
15
15
|
// job just reads whatever the tree looks like at that moment, no hook into git operations at all.
|
|
16
16
|
|
|
17
17
|
function run(cmd, args, cwd) {
|
|
18
|
-
const r = spawnSync(cmd, args, { cwd, encoding: 'utf8', timeout: 120_000 })
|
|
18
|
+
const r = spawnSync(cmd, args, { cwd, encoding: 'utf8', timeout: 120_000, windowsHide: true })
|
|
19
19
|
return r.status === 0 ? (r.stdout || '').trim() : null
|
|
20
20
|
}
|
|
21
21
|
|
|
@@ -78,7 +78,7 @@ export async function runGraphifySync(argv = []) {
|
|
|
78
78
|
return 1
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
const update = spawnSync('graphify', ['update', '.'], { cwd, encoding: 'utf8', timeout: 300_000, maxBuffer: 8 * 1024 * 1024 })
|
|
81
|
+
const update = spawnSync('graphify', ['update', '.'], { cwd, encoding: 'utf8', timeout: 300_000, maxBuffer: 8 * 1024 * 1024, windowsHide: true })
|
|
82
82
|
if (update.status !== 0) {
|
|
83
83
|
process.stderr.write(`cortex-mcp graphify-sync: \`graphify update\` failed:\n${(update.stderr || update.stdout || '').trim()}\n`)
|
|
84
84
|
return 1
|
package/lib/login.mjs
CHANGED
|
@@ -27,7 +27,7 @@ function openBrowser(url) {
|
|
|
27
27
|
process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'cmd' : 'xdg-open'
|
|
28
28
|
const args = process.platform === 'win32' ? ['/c', 'start', '""', url] : [url]
|
|
29
29
|
try {
|
|
30
|
-
const child = spawn(cmd, args, { stdio: 'ignore', detached: true })
|
|
30
|
+
const child = spawn(cmd, args, { stdio: 'ignore', detached: true, windowsHide: true })
|
|
31
31
|
// If the browser cannot be launched we still printed the URL, so this is genuinely non-fatal.
|
|
32
32
|
child.on('error', () => {})
|
|
33
33
|
child.unref()
|
|
@@ -244,6 +244,7 @@ export function spawnObligationWorker(job) {
|
|
|
244
244
|
const child = spawn(process.execPath, [bin, 'extract-obligations'], {
|
|
245
245
|
env: { ...process.env },
|
|
246
246
|
detached: true,
|
|
247
|
+
windowsHide: true,
|
|
247
248
|
stdio: ['pipe', fd, fd],
|
|
248
249
|
})
|
|
249
250
|
child.on('error', () => {})
|
package/lib/server.mjs
CHANGED
|
@@ -5057,6 +5057,53 @@ function renderNudge(payload) {
|
|
|
5057
5057
|
},
|
|
5058
5058
|
)
|
|
5059
5059
|
|
|
5060
|
+
// share_page — a read-only link to one wiki page (ADR-0090; Theron, 2026-10-19). Same shape as share_record:
|
|
5061
|
+
// NOT auto-allowed (a disclosure, so it prompts), and only when the person asks. Never a confidential page.
|
|
5062
|
+
server.registerTool(
|
|
5063
|
+
'share_page',
|
|
5064
|
+
{
|
|
5065
|
+
title: 'Make a share link for a wiki page',
|
|
5066
|
+
description:
|
|
5067
|
+
'Make a read-only link (agnoclast.com/p/…) to ONE wiki page, for anyone — including people outside Agnoclast. ' +
|
|
5068
|
+
'They see the page as it is when they open it: its title and sections; links to other pages show as plain text, ' +
|
|
5069
|
+
'and nothing else in the brain is shared. Signed-in viewers can save their own copy; others can join free. ' +
|
|
5070
|
+
'Allowed for a page you own, or an accessible page you can edit; never a confidential page. Name the page by ' +
|
|
5071
|
+
'node kind + ref (what read_page shows), optionally its tier. The link is shown ONCE — give it to the person ' +
|
|
5072
|
+
'straight away. Only when the person asks to share; never on your own initiative.',
|
|
5073
|
+
inputSchema: {
|
|
5074
|
+
kind: z.string().describe('the page node kind (project, person, org, user, …)'),
|
|
5075
|
+
ref: z.string().describe('the page node ref (uuid)'),
|
|
5076
|
+
tier: z.enum(['accessible', 'scoped', 'reports', 'peers', 'down']).optional().describe('which version, when the node has several'),
|
|
5077
|
+
label: z.string().max(80).optional().describe('a label only you see'),
|
|
5078
|
+
expires_in_days: z.number().int().min(1).max(3650).optional().describe('how long the link works; default: no expiry'),
|
|
5079
|
+
},
|
|
5080
|
+
},
|
|
5081
|
+
async ({ kind, ref, tier, label, expires_in_days }) => {
|
|
5082
|
+
let res
|
|
5083
|
+
try {
|
|
5084
|
+
res = await fetchCortex(`${BASE}/api/brain/page-shares`, {
|
|
5085
|
+
method: 'POST',
|
|
5086
|
+
headers: { Authorization: `Bearer ${TOKEN}`, 'Content-Type': 'application/json' },
|
|
5087
|
+
body: JSON.stringify({ kind, ref, ...(tier ? { tier } : {}), ...(label ? { label } : {}), ...(expires_in_days ? { expiresInDays: expires_in_days } : {}) }),
|
|
5088
|
+
})
|
|
5089
|
+
} catch (e) {
|
|
5090
|
+
return toolError(`Could not make a share link: ${e.message}`)
|
|
5091
|
+
}
|
|
5092
|
+
if ([403, 404, 409].includes(res.status)) {
|
|
5093
|
+
const b = await res.json().catch(() => null)
|
|
5094
|
+
return toolError(`Not shared: ${b?.detail ?? b?.error ?? 'refused'}`)
|
|
5095
|
+
}
|
|
5096
|
+
if (!res.ok) {
|
|
5097
|
+
const d = classify(res.status, res.headers.get('content-type'), await res.text(), res.headers.get('x-vercel-id'))
|
|
5098
|
+
return toolError(`Could not make a share link: ${d.message}`)
|
|
5099
|
+
}
|
|
5100
|
+
const out = await res.json()
|
|
5101
|
+
return { content: [{ type: 'text', text:
|
|
5102
|
+
`Share link for the page "${out.title}":\n${out.url}\n` +
|
|
5103
|
+
`Read-only, live (they see the page as it is when they open it). This link is shown only once. share id: ${out.share.id}` }] }
|
|
5104
|
+
},
|
|
5105
|
+
)
|
|
5106
|
+
|
|
5060
5107
|
// set_record_notes — write the NOTES layer of one of your own transcripts (Theron, 2026-10-08: "two
|
|
5061
5108
|
// layers … a summary and notes from the transcript … the full transcript"). The server keeps the
|
|
5062
5109
|
// before-state in record history and refuses anything that is not your own kept transcript.
|
package/lib/uninstall.mjs
CHANGED
|
@@ -221,17 +221,17 @@ export function stripCodexCortexTables(text) {
|
|
|
221
221
|
}
|
|
222
222
|
|
|
223
223
|
function safeReaddir(dir) {
|
|
224
|
-
try { return execFileSync('ls', ['-1', dir], { encoding: 'utf8' }).split('\n').filter(Boolean) } catch { return [] }
|
|
224
|
+
try { return execFileSync('ls', ['-1', dir], { encoding: 'utf8', windowsHide: true }).split('\n').filter(Boolean) } catch { return [] }
|
|
225
225
|
}
|
|
226
226
|
function npxCacheDirs() {
|
|
227
227
|
const base = join(HOME, '.npm', '_npx')
|
|
228
228
|
const out = []
|
|
229
229
|
for (const d of safeReaddir(base)) {
|
|
230
230
|
const dir = join(base, d)
|
|
231
|
-
try { if (execFileSync('grep', ['-rlE', '@theronap/(cortex|agnoclast)-mcp', join(dir, 'package.json')], { encoding: 'utf8' }).trim()) out.push(dir) } catch { /* not one of ours */ }
|
|
231
|
+
try { if (execFileSync('grep', ['-rlE', '@theronap/(cortex|agnoclast)-mcp', join(dir, 'package.json')], { encoding: 'utf8', windowsHide: true }).trim()) out.push(dir) } catch { /* not one of ours */ }
|
|
232
232
|
}
|
|
233
233
|
return out
|
|
234
234
|
}
|
|
235
|
-
function tryExec(cmd, args) { try { execFileSync(cmd, args, { stdio: 'ignore' }) } catch { /* best-effort */ } }
|
|
236
|
-
function tryExecOut(cmd, args) { try { return execFileSync(cmd, args, { encoding: 'utf8' }) } catch { return null } }
|
|
237
|
-
function tryExecIn(cmd, args, input) { try { execFileSync(cmd, args, { input }) } catch { /* best-effort */ } }
|
|
235
|
+
function tryExec(cmd, args) { try { execFileSync(cmd, args, { stdio: 'ignore', windowsHide: true }) } catch { /* best-effort */ } }
|
|
236
|
+
function tryExecOut(cmd, args) { try { return execFileSync(cmd, args, { encoding: 'utf8', windowsHide: true }) } catch { return null } }
|
|
237
|
+
function tryExecIn(cmd, args, input) { try { execFileSync(cmd, args, { input, windowsHide: true }) } catch { /* best-effort */ } }
|
package/lib/working_files.mjs
CHANGED
|
@@ -383,7 +383,7 @@ export function findTranscript(sessionId, projectsDir = join(homedir(), '.claude
|
|
|
383
383
|
function gitRoot(dir) {
|
|
384
384
|
try {
|
|
385
385
|
return execFileSync('git', ['-C', dir, 'rev-parse', '--show-toplevel'], {
|
|
386
|
-
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'],
|
|
386
|
+
encoding: 'utf8', timeout: 2000, stdio: ['ignore', 'pipe', 'ignore'], windowsHide: true,
|
|
387
387
|
}).trim() || null
|
|
388
388
|
} catch {
|
|
389
389
|
return null
|
package/package.json
CHANGED