#!/usr/bin/env node
/**
* stream-tool.js — Stream tool calls (Edit/Write) into AtomSpace in real-time
*
* Runs as async PostToolUse hook. On Edit/Write, creates an edge linking
* the file to the current session. Used by preflight queries to detect
* "you just edited this file" and "correction just happened near this file."
*/
const fs = require('fs')
const { isCogServerUp, evalScheme } = require('./cogserver')
const SESSION_FILE = '/tmp/claude-atomspace-session'
function esc (s) {
return String(s || '').replace(/\\/g, '\\\\').replace(/"/g, '\\"')
}
function readSessionId () {
try { return fs.readFileSync(SESSION_FILE, 'utf8').trim() } catch { return null }
}
function normalizePath (p) {
if (!p) return ''
return p
.replace(/^\/storage\/projects\/code\/hypercore-kb\/hyper-fleeky\//, '')
.replace(/^\/storage\/projects\/code\/hypercore-kb\//, '')
.replace(/^\/storage\/projects\/code\//, '')
}
async function main () {
let input = ''
for await (const chunk of process.stdin) input += chunk
let data
try { data = JSON.parse(input) } catch { process.exit(0) }
const toolName = data.tool_name || data.tool
if (toolName !== 'Edit' && toolName !== 'Write') process.exit(0)
const toolInput = data.tool_input || {}
const filePath = toolInput.file_path || ''
if (!filePath) process.exit(0)
// Only track edits that actually succeeded (check tool_response if available)
const response = data.tool_response || {}
if (response.isError) process.exit(0)
if (!await isCogServerUp()) process.exit(0)
const sessionId = readSessionId()
if (!sessionId) process.exit(0)
const normalPath = normalizePath(filePath)
if (!normalPath) process.exit(0)
const timestamp = Math.floor(Date.now() / 1000)
const sessionNode = `(ConceptNode "session:${sessionId}")`
const fileNode = `(ConceptNode "file:${esc(normalPath)}")`
const editId = `live:edit:${sessionId}:${timestamp}`
const editNode = `(ConceptNode "${esc(editId)}")`
const exprs = [
fileNode,
editNode,
`(cog-set-value! ${editNode} (Predicate "kind") (StringValue "${toolName}"))`,
`(cog-set-value! ${editNode} (Predicate "timestamp") (FloatValue ${timestamp}))`,
// Link edit event → session
`(EdgeLink (BondNode "edit-in-session") (ListLink ${editNode} ${sessionNode}))`,
// Link edit event → file
`(EdgeLink (BondNode "edit-of-file") (ListLink ${editNode} ${fileNode}))`,
// Direct: session edited file (deduplicated because atoms are unique)
`(EdgeLink (BondNode "session-edited") (ListLink ${sessionNode} ${fileNode}))`
]
try {
await evalScheme('(begin ' + exprs.join(' ') + ')')
} catch {}
process.exit(0)
}
main().catch(() => process.exit(0))