@remnic/plugin-codex 1.0.0
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/.codex-plugin/plugin.json +8 -0
- package/.mcp.json +12 -0
- package/LICENSE +21 -0
- package/bin/materialize.cjs +212 -0
- package/hooks/bin/post-tool-observe.sh +164 -0
- package/hooks/bin/session-end.sh +164 -0
- package/hooks/bin/session-start.sh +151 -0
- package/hooks/bin/user-prompt-recall.sh +114 -0
- package/hooks/hooks.json +52 -0
- package/memories_extensions/remnic/instructions.md +160 -0
- package/memories_extensions/remnic/resources/namespace-cheatsheet.md +48 -0
- package/package.json +35 -0
- package/skills/remnic-entities/SKILL.md +51 -0
- package/skills/remnic-memory-workflow/SKILL.md +61 -0
- package/skills/remnic-recall/SKILL.md +51 -0
- package/skills/remnic-remember/SKILL.md +56 -0
- package/skills/remnic-search/SKILL.md +51 -0
- package/skills/remnic-status/SKILL.md +51 -0
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic SessionStart hook for Codex.
|
|
3
|
+
# Recalls project context and user preferences at session start.
|
|
4
|
+
# Tries auto mode (45s) then falls back to minimal mode (20s).
|
|
5
|
+
# Starts daemon if not running.
|
|
6
|
+
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
ensure_migrated() {
|
|
10
|
+
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
11
|
+
return 0
|
|
12
|
+
fi
|
|
13
|
+
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
14
|
+
return 0
|
|
15
|
+
fi
|
|
16
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
17
|
+
remnic migrate >/dev/null 2>&1 || true
|
|
18
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
19
|
+
engram migrate >/dev/null 2>&1 || true
|
|
20
|
+
fi
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
ensure_migrated
|
|
24
|
+
|
|
25
|
+
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
26
|
+
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
27
|
+
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
28
|
+
REMNIC_HEALTH_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/health"
|
|
29
|
+
TOKEN_FILES=("${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json")
|
|
30
|
+
|
|
31
|
+
LOG="${HOME}/.remnic/logs/remnic-session-recall.log"
|
|
32
|
+
mkdir -p "$(dirname "$LOG")"
|
|
33
|
+
log() { echo "$(date '+%F %T') [codex-session-start] $*" >> "$LOG"; }
|
|
34
|
+
|
|
35
|
+
# Read token from per-plugin token store
|
|
36
|
+
REMNIC_TOKEN=""
|
|
37
|
+
for TOKEN_FILE in "${TOKEN_FILES[@]}"; do
|
|
38
|
+
[ ! -f "$TOKEN_FILE" ] && continue
|
|
39
|
+
REMNIC_TOKEN="$(node -e "
|
|
40
|
+
const store = JSON.parse(require('fs').readFileSync(process.argv[1],'utf8'));
|
|
41
|
+
const tokens = store.tokens || [];
|
|
42
|
+
const cxc = tokens.find(t => t.connector === 'codex-cli');
|
|
43
|
+
const cx = tokens.find(t => t.connector === 'codex');
|
|
44
|
+
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
45
|
+
let tok = (cxc && cxc.token) || (cx && cx.token) || (oc && oc.token) || '';
|
|
46
|
+
if (!tok) { tok = store['codex-cli'] || store['codex'] || store['openclaw'] || ''; }
|
|
47
|
+
process.stdout.write(tok);
|
|
48
|
+
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
49
|
+
[ -n "$REMNIC_TOKEN" ] && break
|
|
50
|
+
done
|
|
51
|
+
|
|
52
|
+
# Fallback to env var
|
|
53
|
+
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
54
|
+
|
|
55
|
+
INPUT="$(cat)"
|
|
56
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
57
|
+
CWD="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.cwd||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
58
|
+
PROJECT_NAME="$(basename "$CWD" 2>/dev/null || echo "unknown")"
|
|
59
|
+
|
|
60
|
+
log "session=$SESSION_ID project=$PROJECT_NAME"
|
|
61
|
+
|
|
62
|
+
# Health check — start daemon if not running
|
|
63
|
+
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
64
|
+
log "daemon not responding, attempting start..."
|
|
65
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
66
|
+
remnic daemon start >/dev/null 2>&1 &
|
|
67
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
68
|
+
engram daemon start >/dev/null 2>&1 &
|
|
69
|
+
fi
|
|
70
|
+
sleep 2
|
|
71
|
+
if ! curl -sf --max-time 2 "$REMNIC_HEALTH_URL" >/dev/null 2>&1; then
|
|
72
|
+
log "daemon still not responding after start attempt"
|
|
73
|
+
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: daemon not running — start with: remnic daemon start]"}}'
|
|
74
|
+
exit 0
|
|
75
|
+
fi
|
|
76
|
+
fi
|
|
77
|
+
|
|
78
|
+
if [ -z "$REMNIC_TOKEN" ]; then
|
|
79
|
+
log "skipping: no token found"
|
|
80
|
+
echo '{"continue":true,"hookSpecificOutput":{"hookEventName":"SessionStart","additionalContext":"[Remnic: no auth token — run: remnic connectors install codex-cli]"}}'
|
|
81
|
+
exit 0
|
|
82
|
+
fi
|
|
83
|
+
|
|
84
|
+
QUERY="Starting a new coding session in project: ${PROJECT_NAME}. Recall relevant memories, preferences, decisions, patterns, and context about this project and the user."
|
|
85
|
+
|
|
86
|
+
REQUEST_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
87
|
+
query: process.argv[1],
|
|
88
|
+
sessionKey: process.argv[2],
|
|
89
|
+
topK: 12,
|
|
90
|
+
mode: 'auto'
|
|
91
|
+
}))" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
92
|
+
|
|
93
|
+
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
94
|
+
|
|
95
|
+
log "attempting full recall (auto mode)..."
|
|
96
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 45 \
|
|
97
|
+
-X POST "$REMNIC_URL" \
|
|
98
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
99
|
+
-H "Content-Type: application/json" \
|
|
100
|
+
-H "X-Engram-Client-Id: codex" \
|
|
101
|
+
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
102
|
+
CURL_EXIT=$?
|
|
103
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
104
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
105
|
+
|
|
106
|
+
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
107
|
+
log "full recall failed (curl=$CURL_EXIT http=$HTTP_STATUS) — falling back to minimal"
|
|
108
|
+
MINIMAL_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
109
|
+
query: process.argv[1],
|
|
110
|
+
sessionKey: process.argv[2],
|
|
111
|
+
topK: 8,
|
|
112
|
+
mode: 'minimal'
|
|
113
|
+
}))" "$QUERY" "$SESSION_ID" 2>/dev/null)"
|
|
114
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
115
|
+
-X POST "$REMNIC_URL" \
|
|
116
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
117
|
+
-H "Content-Type: application/json" \
|
|
118
|
+
-H "X-Engram-Client-Id: codex" \
|
|
119
|
+
-d "${MINIMAL_BODY:-$REQUEST_BODY}" 2>/dev/null)"
|
|
120
|
+
CURL_EXIT=$?
|
|
121
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
122
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
123
|
+
[[ "$CURL_EXIT" -eq 0 && "$HTTP_STATUS" =~ ^2 ]] && log "minimal recall succeeded" || { log "minimal recall also failed"; CURL_EXIT=1; }
|
|
124
|
+
fi
|
|
125
|
+
|
|
126
|
+
if [ $CURL_EXIT -eq 0 ] && [[ "$HTTP_STATUS" =~ ^2 ]] && [ -n "$RESPONSE" ]; then
|
|
127
|
+
CONTEXT="$(node -e "
|
|
128
|
+
const d = JSON.parse(process.argv[1]);
|
|
129
|
+
const ctx = d.context || '';
|
|
130
|
+
const count = d.count || 0;
|
|
131
|
+
const mode = d.mode || '';
|
|
132
|
+
if (ctx) {
|
|
133
|
+
const label = '[Remnic Memory Recall — ' + count + ' memories' + (mode ? ', ' + mode + ' mode' : '') + ']';
|
|
134
|
+
process.stdout.write(label + '\n\n' + ctx);
|
|
135
|
+
} else {
|
|
136
|
+
process.stdout.write('[Remnic: no relevant memories found for this session]');
|
|
137
|
+
}
|
|
138
|
+
" "$RESPONSE" 2>/dev/null || echo "[Remnic: recall parse error]")"
|
|
139
|
+
log "recall complete: $(echo "$CONTEXT" | head -1)"
|
|
140
|
+
else
|
|
141
|
+
CONTEXT="[Remnic: server unreachable — continuing without memory recall]"
|
|
142
|
+
log "$CONTEXT"
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
node -e "
|
|
146
|
+
const context = process.argv[1];
|
|
147
|
+
process.stdout.write(JSON.stringify({
|
|
148
|
+
continue: true,
|
|
149
|
+
hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: context }
|
|
150
|
+
}));
|
|
151
|
+
" "$CONTEXT"
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Remnic UserPromptSubmit hook for Codex.
|
|
3
|
+
# Recalls per-prompt context using the user's message as query.
|
|
4
|
+
# Skips short prompts (<4 words). Minimal mode, 20s timeout.
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
ensure_migrated() {
|
|
9
|
+
if [ -f "${HOME}/.remnic/.migrated-from-engram" ]; then
|
|
10
|
+
return 0
|
|
11
|
+
fi
|
|
12
|
+
if [ ! -d "${HOME}/.engram" ] && [ ! -f "${HOME}/.config/engram/config.json" ]; then
|
|
13
|
+
return 0
|
|
14
|
+
fi
|
|
15
|
+
if command -v remnic >/dev/null 2>&1; then
|
|
16
|
+
remnic migrate >/dev/null 2>&1 || true
|
|
17
|
+
elif command -v engram >/dev/null 2>&1; then
|
|
18
|
+
engram migrate >/dev/null 2>&1 || true
|
|
19
|
+
fi
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
ensure_migrated
|
|
23
|
+
|
|
24
|
+
REMNIC_HOST="${REMNIC_HOST:-${ENGRAM_HOST:-127.0.0.1}}"
|
|
25
|
+
REMNIC_PORT="${REMNIC_PORT:-${ENGRAM_PORT:-4318}}"
|
|
26
|
+
REMNIC_URL="http://${REMNIC_HOST}:${REMNIC_PORT}/engram/v1/recall"
|
|
27
|
+
|
|
28
|
+
LOG="${HOME}/.remnic/logs/remnic-user-prompt-recall.log"
|
|
29
|
+
mkdir -p "$(dirname "$LOG")"
|
|
30
|
+
log() { echo "$(date '+%F %T') [codex-user-prompt] $*" >> "$LOG"; }
|
|
31
|
+
|
|
32
|
+
# Read token
|
|
33
|
+
REMNIC_TOKEN=""
|
|
34
|
+
for TOKEN_FILE in "${HOME}/.remnic/tokens.json" "${HOME}/.engram/tokens.json"; do
|
|
35
|
+
[ ! -f "$TOKEN_FILE" ] && continue
|
|
36
|
+
REMNIC_TOKEN="$(node -e "
|
|
37
|
+
const fs = require('fs');
|
|
38
|
+
const tokenFile = process.argv[1];
|
|
39
|
+
const store = JSON.parse(fs.readFileSync(tokenFile, 'utf8'));
|
|
40
|
+
const tokens = store.tokens || [];
|
|
41
|
+
const cxc = tokens.find(t => t.connector === 'codex-cli');
|
|
42
|
+
const cx = tokens.find(t => t.connector === 'codex');
|
|
43
|
+
const oc = tokens.find(t => t.connector === 'openclaw');
|
|
44
|
+
let tok = (cxc && cxc.token) || (cx && cx.token) || (oc && oc.token) || '';
|
|
45
|
+
if (!tok) { tok = store['codex-cli'] || store['codex'] || store['openclaw'] || ''; }
|
|
46
|
+
process.stdout.write(tok);
|
|
47
|
+
" "$TOKEN_FILE" 2>/dev/null || echo "")"
|
|
48
|
+
[ -n "$REMNIC_TOKEN" ] && break
|
|
49
|
+
done
|
|
50
|
+
[ -z "$REMNIC_TOKEN" ] && REMNIC_TOKEN="${OPENCLAW_REMNIC_ACCESS_TOKEN:-${OPENCLAW_ENGRAM_ACCESS_TOKEN:-}}"
|
|
51
|
+
|
|
52
|
+
INPUT="$(cat)"
|
|
53
|
+
|
|
54
|
+
if [ -z "$REMNIC_TOKEN" ]; then
|
|
55
|
+
echo '{"continue":true}'
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
SESSION_ID="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.session_id||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
60
|
+
PROMPT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(d.prompt||'')" "$INPUT" 2>/dev/null || echo "")"
|
|
61
|
+
|
|
62
|
+
# Skip very short prompts
|
|
63
|
+
WORD_COUNT="$(echo "$PROMPT" | wc -w | tr -d ' ')"
|
|
64
|
+
if [ "$WORD_COUNT" -lt 4 ]; then
|
|
65
|
+
echo '{"continue":true}'
|
|
66
|
+
exit 0
|
|
67
|
+
fi
|
|
68
|
+
|
|
69
|
+
log "session=$SESSION_ID words=$WORD_COUNT"
|
|
70
|
+
|
|
71
|
+
REQUEST_BODY="$(node -e "process.stdout.write(JSON.stringify({
|
|
72
|
+
query: process.argv[1],
|
|
73
|
+
sessionKey: process.argv[2],
|
|
74
|
+
topK: 8,
|
|
75
|
+
mode: 'minimal'
|
|
76
|
+
}))" "$PROMPT" "$SESSION_ID" 2>/dev/null)"
|
|
77
|
+
|
|
78
|
+
[ -z "$REQUEST_BODY" ] && echo '{"continue":true}' && exit 0
|
|
79
|
+
|
|
80
|
+
RAW="$(curl -s -w "\n%{http_code}" --max-time 20 \
|
|
81
|
+
-X POST "$REMNIC_URL" \
|
|
82
|
+
-H "Authorization: Bearer ${REMNIC_TOKEN}" \
|
|
83
|
+
-H "Content-Type: application/json" \
|
|
84
|
+
-H "X-Engram-Client-Id: codex" \
|
|
85
|
+
-d "$REQUEST_BODY" 2>/dev/null)"
|
|
86
|
+
CURL_EXIT=$?
|
|
87
|
+
HTTP_STATUS="$(echo "$RAW" | tail -1)"
|
|
88
|
+
RESPONSE="$(echo "$RAW" | sed '$d')"
|
|
89
|
+
|
|
90
|
+
if [ $CURL_EXIT -ne 0 ] || ! [[ "$HTTP_STATUS" =~ ^2 ]] || [ -z "$RESPONSE" ]; then
|
|
91
|
+
log "recall failed (curl=$CURL_EXIT http=$HTTP_STATUS)"
|
|
92
|
+
echo '{"continue":true}'
|
|
93
|
+
exit 0
|
|
94
|
+
fi
|
|
95
|
+
|
|
96
|
+
node -e "
|
|
97
|
+
const d = JSON.parse(process.argv[1]);
|
|
98
|
+
const ctx = d.context || '';
|
|
99
|
+
const count = d.count || 0;
|
|
100
|
+
if (!ctx || count === 0) {
|
|
101
|
+
process.stdout.write(JSON.stringify({continue: true}));
|
|
102
|
+
} else {
|
|
103
|
+
process.stdout.write(JSON.stringify({
|
|
104
|
+
continue: true,
|
|
105
|
+
hookSpecificOutput: {
|
|
106
|
+
hookEventName: 'UserPromptSubmit',
|
|
107
|
+
additionalContext: '<remnic-memory count=\"' + count + '\">\n' + ctx + '\n</remnic-memory>'
|
|
108
|
+
}
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
" "$RESPONSE" 2>/dev/null || echo '{"continue":true}'
|
|
112
|
+
|
|
113
|
+
COUNT="$(node -e "const d=JSON.parse(process.argv[1]); process.stdout.write(String(d.count||0))" "$RESPONSE" 2>/dev/null || echo "?")"
|
|
114
|
+
log "done: ${COUNT} memories injected"
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "./hooks/bin/session-start.sh",
|
|
10
|
+
"timeout": 45000
|
|
11
|
+
}
|
|
12
|
+
]
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"PostToolUse": [
|
|
16
|
+
{
|
|
17
|
+
"matcher": "Bash",
|
|
18
|
+
"hooks": [
|
|
19
|
+
{
|
|
20
|
+
"type": "command",
|
|
21
|
+
"command": "./hooks/bin/post-tool-observe.sh",
|
|
22
|
+
"timeout": 10000
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
"UserPromptSubmit": [
|
|
28
|
+
{
|
|
29
|
+
"matcher": "*",
|
|
30
|
+
"hooks": [
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "./hooks/bin/user-prompt-recall.sh",
|
|
34
|
+
"timeout": 20000
|
|
35
|
+
}
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
],
|
|
39
|
+
"Stop": [
|
|
40
|
+
{
|
|
41
|
+
"matcher": "*",
|
|
42
|
+
"hooks": [
|
|
43
|
+
{
|
|
44
|
+
"type": "command",
|
|
45
|
+
"command": "./hooks/bin/session-end.sh",
|
|
46
|
+
"timeout": 30000
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# Remnic Memory Extension for Codex
|
|
2
|
+
|
|
3
|
+
You are the Codex consolidation sub-agent. This document tells you how to treat
|
|
4
|
+
Remnic as an authoritative local memory source while you summarize a session
|
|
5
|
+
and build the compacted MEMORY.md output.
|
|
6
|
+
|
|
7
|
+
Remnic is a local-first, file-backed memory system. All Remnic content that
|
|
8
|
+
matters to you lives on disk as plain Markdown — you do not need a network,
|
|
9
|
+
an MCP server, or the `remnic` CLI to read it. You are running inside the
|
|
10
|
+
Codex phase-2 sandbox: no approvals, no network, local reads and local writes
|
|
11
|
+
only.
|
|
12
|
+
|
|
13
|
+
## What Remnic is authoritative for
|
|
14
|
+
|
|
15
|
+
Treat Remnic content as a trusted, high-signal memory source when you need any
|
|
16
|
+
of the following:
|
|
17
|
+
|
|
18
|
+
- **Stable user preferences** — coding conventions, tool choices, style
|
|
19
|
+
guides, phrasing, commit message format, review etiquette.
|
|
20
|
+
- **Project conventions** — folder layout, naming rules, test runners,
|
|
21
|
+
build commands, branching strategy, deployment workflow.
|
|
22
|
+
- **Reusable workflows and skills** — documented runbooks, procedures, and
|
|
23
|
+
"how we do X here" notes that should survive across sessions.
|
|
24
|
+
- **Long-lived decisions** — architecture calls, library choices, explicit
|
|
25
|
+
"we decided not to do X" entries.
|
|
26
|
+
- **Entities the user cares about** — projects, services, people, API
|
|
27
|
+
contracts, integrations mentioned by name.
|
|
28
|
+
|
|
29
|
+
If the current session touches any of these areas, consult Remnic before you
|
|
30
|
+
finalize the consolidated MEMORY.md output.
|
|
31
|
+
|
|
32
|
+
## When NOT to consult Remnic
|
|
33
|
+
|
|
34
|
+
Do not waste filesystem tool calls on Remnic when:
|
|
35
|
+
|
|
36
|
+
- The session is purely transient (one-off shell commands, throwaway
|
|
37
|
+
debugging with no lasting conclusion).
|
|
38
|
+
- You already have the information in the current session transcript and
|
|
39
|
+
Remnic would only duplicate it.
|
|
40
|
+
- The user has explicitly asked you to ignore memory or work from a clean
|
|
41
|
+
slate.
|
|
42
|
+
- You are summarizing a session that never reached a decision, a preference,
|
|
43
|
+
or a durable artifact worth recording.
|
|
44
|
+
|
|
45
|
+
Prefer a single targeted read over broad directory walks.
|
|
46
|
+
|
|
47
|
+
## Where Remnic content lives on disk
|
|
48
|
+
|
|
49
|
+
Resolve the Remnic memory base in this order:
|
|
50
|
+
|
|
51
|
+
1. If the environment variable `REMNIC_HOME` is set, use
|
|
52
|
+
`$REMNIC_HOME/memories/`.
|
|
53
|
+
2. Otherwise use `~/.remnic/memories/`.
|
|
54
|
+
|
|
55
|
+
Under that base, memories are organized by **namespace**:
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
<remnic-home>/memories/<namespace>/
|
|
59
|
+
├── MEMORY.md # compact top-of-mind memory
|
|
60
|
+
├── memory_summary.md # optional longer human-readable summary
|
|
61
|
+
├── skills/
|
|
62
|
+
│ └── <skill-name>/
|
|
63
|
+
│ └── SKILL.md # reusable workflow
|
|
64
|
+
└── rollout_summaries/
|
|
65
|
+
└── *.md # per-session rollup notes
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Canonical files you should prefer, in order:
|
|
69
|
+
|
|
70
|
+
1. `MEMORY.md` — the current compact memory. Read this first.
|
|
71
|
+
2. `memory_summary.md` — longer-form summary if it exists.
|
|
72
|
+
3. `skills/<name>/SKILL.md` — for reusable procedures relevant to the task.
|
|
73
|
+
4. `rollout_summaries/*.md` — recent session notes, newest first.
|
|
74
|
+
|
|
75
|
+
If none of the above exist for the resolved namespace, Remnic simply has
|
|
76
|
+
nothing to contribute — move on without error.
|
|
77
|
+
|
|
78
|
+
## Resolving the namespace
|
|
79
|
+
|
|
80
|
+
Remnic uses **cwd-derived namespaces** by default. Apply this rule when
|
|
81
|
+
choosing which namespace directory to read:
|
|
82
|
+
|
|
83
|
+
1. Start from the session's working directory (the `cwd` Codex used for the
|
|
84
|
+
session you are consolidating).
|
|
85
|
+
2. Walk upward looking for a project anchor: `.git`, `package.json`,
|
|
86
|
+
`pyproject.toml`, `Cargo.toml`, `go.mod`, or an explicit
|
|
87
|
+
`.remnic/namespace` file.
|
|
88
|
+
3. The namespace is the basename of that anchor directory, lowercased and
|
|
89
|
+
with spaces replaced by `-`.
|
|
90
|
+
4. If you cannot find an anchor, fall back to the namespace `default`.
|
|
91
|
+
5. In addition to the project namespace, always also check the `shared`
|
|
92
|
+
namespace for cross-project preferences (e.g.
|
|
93
|
+
`<remnic-home>/memories/shared/MEMORY.md`). If it exists, read it.
|
|
94
|
+
|
|
95
|
+
If a session explicitly mentions a different Remnic namespace (for example,
|
|
96
|
+
the user says "use the `work` namespace"), prefer that explicit value over
|
|
97
|
+
the cwd-derived one.
|
|
98
|
+
|
|
99
|
+
## How to cite Remnic memories in your output
|
|
100
|
+
|
|
101
|
+
When a piece of the consolidated memory you are writing comes from a Remnic
|
|
102
|
+
file, cite it using the Codex memory citation block format so the user can
|
|
103
|
+
trace the source:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
<oai-mem-citation path="<path-relative-to-remnic-memory-base>" />
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
The path must be **relative to the Remnic memory base** (the directory named
|
|
110
|
+
`memories/` under `<remnic-home>`), not absolute. Examples:
|
|
111
|
+
|
|
112
|
+
- `<oai-mem-citation path="default/MEMORY.md" />`
|
|
113
|
+
- `<oai-mem-citation path="my-project/skills/deploy/SKILL.md" />`
|
|
114
|
+
- `<oai-mem-citation path="shared/memory_summary.md" />`
|
|
115
|
+
|
|
116
|
+
Cite each distinct source once near the fact it supports. Do not invent
|
|
117
|
+
citations for files you have not actually read.
|
|
118
|
+
|
|
119
|
+
## Sandboxing rules (hard constraints)
|
|
120
|
+
|
|
121
|
+
You are running in the Codex phase-2 consolidation sandbox. These rules are
|
|
122
|
+
non-negotiable:
|
|
123
|
+
|
|
124
|
+
- **No network.** Do not attempt HTTP calls, MCP connections, or anything
|
|
125
|
+
that reaches outside this machine.
|
|
126
|
+
- **No `remnic` CLI invocation.** Do not shell out to `remnic`, `engram`,
|
|
127
|
+
`qmd`, or any daemon. Use filesystem reads only.
|
|
128
|
+
- **No MCP tool calls.** You must not call `remnic.recall`,
|
|
129
|
+
`remnic.memory_store`, or any other MCP-backed tool. They are not
|
|
130
|
+
available in this sandbox.
|
|
131
|
+
- **Local writes are allowed** only where Codex's sandbox policy already
|
|
132
|
+
permits them (typically the Codex memories output folder). Do not write
|
|
133
|
+
into the Remnic memory directory — it is read-only from your perspective.
|
|
134
|
+
- **Respect missing files.** If a file does not exist, move on silently.
|
|
135
|
+
Never create placeholder Remnic files.
|
|
136
|
+
|
|
137
|
+
## Failure handling
|
|
138
|
+
|
|
139
|
+
- Remnic base directory missing: no-op. Remnic has nothing for this session.
|
|
140
|
+
- Namespace directory missing: try the `shared` namespace, then give up.
|
|
141
|
+
- Malformed file: skip it and continue.
|
|
142
|
+
- Never block consolidation on a Remnic read error.
|
|
143
|
+
|
|
144
|
+
## Quick recipe
|
|
145
|
+
|
|
146
|
+
For a typical consolidation run:
|
|
147
|
+
|
|
148
|
+
1. Resolve `<remnic-home>` from `$REMNIC_HOME` or `~/.remnic`.
|
|
149
|
+
2. Resolve `<namespace>` from the session cwd using the rule above.
|
|
150
|
+
3. Read `<remnic-home>/memories/<namespace>/MEMORY.md` if present.
|
|
151
|
+
4. Read `<remnic-home>/memories/shared/MEMORY.md` if present.
|
|
152
|
+
5. If the session produced or used a named workflow, read
|
|
153
|
+
`<remnic-home>/memories/<namespace>/skills/<name>/SKILL.md`.
|
|
154
|
+
6. If you need more context, peek at the newest file under
|
|
155
|
+
`<remnic-home>/memories/<namespace>/rollout_summaries/`.
|
|
156
|
+
7. Fold confirmed facts and preferences into the consolidated output and
|
|
157
|
+
cite them with `<oai-mem-citation />`.
|
|
158
|
+
|
|
159
|
+
That is the whole extension. Keep it tight, cite your sources, and never
|
|
160
|
+
invent Remnic content you did not read.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# Remnic Namespace Cheatsheet
|
|
2
|
+
|
|
3
|
+
Remnic partitions memories into **namespaces** so multiple projects and
|
|
4
|
+
contexts can share a single Remnic home without bleeding into each other.
|
|
5
|
+
When the Codex consolidation sub-agent reads Remnic files, it has to pick
|
|
6
|
+
the right namespace directory for the session it is summarizing. This
|
|
7
|
+
cheatsheet documents the resolution rule.
|
|
8
|
+
|
|
9
|
+
## Resolution rule (in order)
|
|
10
|
+
|
|
11
|
+
1. **Explicit override in the session.** If the user or the transcript
|
|
12
|
+
explicitly names a Remnic namespace, use that value verbatim.
|
|
13
|
+
2. **Project anchor walk.** Starting from the session's working directory,
|
|
14
|
+
walk upward until you find any of:
|
|
15
|
+
- `.git/`
|
|
16
|
+
- `.remnic/namespace` file (highest priority if present)
|
|
17
|
+
- `package.json`
|
|
18
|
+
- `pyproject.toml`
|
|
19
|
+
- `Cargo.toml`
|
|
20
|
+
- `go.mod`
|
|
21
|
+
Use the basename of the anchor directory, lowercased, with whitespace
|
|
22
|
+
replaced by `-`.
|
|
23
|
+
3. **Fallback.** If nothing above matches, use the namespace `default`.
|
|
24
|
+
4. **Shared overlay.** In addition to the resolved namespace, always also
|
|
25
|
+
check the `shared` namespace for cross-project content.
|
|
26
|
+
|
|
27
|
+
## Examples
|
|
28
|
+
|
|
29
|
+
| Session cwd | Anchor | Namespace |
|
|
30
|
+
|------------------------------------------|----------------------|-----------------|
|
|
31
|
+
| `/home/user/code/my-app/src` | `/home/user/code/my-app/.git` | `my-app` |
|
|
32
|
+
| `/home/user/code/Data Pipeline` | `.git` | `data-pipeline` |
|
|
33
|
+
| `/tmp/scratch` | (none) | `default` |
|
|
34
|
+
| `/work/research/` (contains `.remnic/namespace` = `lab`) | `.remnic/namespace` | `lab` |
|
|
35
|
+
|
|
36
|
+
## Why it matters
|
|
37
|
+
|
|
38
|
+
If the consolidation agent reads from the wrong namespace, it will either
|
|
39
|
+
miss relevant project-specific memories (false negative) or drag unrelated
|
|
40
|
+
content into the summary (false positive). Getting the namespace right keeps
|
|
41
|
+
Remnic's signal-to-noise ratio high.
|
|
42
|
+
|
|
43
|
+
## What the extension does with this
|
|
44
|
+
|
|
45
|
+
The consolidation sub-agent reads `MEMORY.md`, `memory_summary.md`, any
|
|
46
|
+
relevant `skills/<name>/SKILL.md`, and newest `rollout_summaries/*.md`
|
|
47
|
+
under the resolved namespace, plus the `shared` namespace overlay. All
|
|
48
|
+
reads are filesystem-only — no CLI, no network, no MCP.
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@remnic/plugin-codex",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Remnic memory plugin for Codex CLI — hooks, skills, MCP integration",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/joshuaswarren/remnic.git",
|
|
10
|
+
"directory": "packages/plugin-codex"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"remnic",
|
|
14
|
+
"memory",
|
|
15
|
+
"codex",
|
|
16
|
+
"openai",
|
|
17
|
+
"plugin",
|
|
18
|
+
"ai-agent"
|
|
19
|
+
],
|
|
20
|
+
"publishConfig": {
|
|
21
|
+
"access": "public",
|
|
22
|
+
"provenance": true
|
|
23
|
+
},
|
|
24
|
+
"files": [
|
|
25
|
+
".codex-plugin",
|
|
26
|
+
"bin",
|
|
27
|
+
"hooks",
|
|
28
|
+
"skills",
|
|
29
|
+
"memories_extensions",
|
|
30
|
+
".mcp.json"
|
|
31
|
+
],
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@remnic/core": "^1.0.3"
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-entities
|
|
3
|
+
description: Browse entities in the Remnic knowledge graph and surface their facts and relationships. Trigger phrases include "tell me about the entity", "look up", "what do we know about".
|
|
4
|
+
allowed-tools:
|
|
5
|
+
- remnic_entity_get
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## When to use
|
|
9
|
+
|
|
10
|
+
Use when the user names a specific project, person, service, or concept and asks what is known about it. Entities are the graph-shaped view of memory — structured facts and relationships rather than free text.
|
|
11
|
+
|
|
12
|
+
Triggers:
|
|
13
|
+
|
|
14
|
+
- "Tell me about the entity …"
|
|
15
|
+
- "Look up …"
|
|
16
|
+
- "What do we know about project/service/tool X?"
|
|
17
|
+
- Any reference to a named thing that could have linked facts.
|
|
18
|
+
|
|
19
|
+
## Inputs
|
|
20
|
+
|
|
21
|
+
- `name` (required) — canonical entity name, ideally as the user wrote it.
|
|
22
|
+
- Optional: entity type hint (project, person, service, tool).
|
|
23
|
+
|
|
24
|
+
## Procedure
|
|
25
|
+
|
|
26
|
+
1. Extract the entity name from the user's message. Preserve original capitalization unless ambiguous.
|
|
27
|
+
2. Call `remnic_entity_get` with that name.
|
|
28
|
+
3. If the entity is found, present its facts and relationships in a short structured view (facts, relations, last updated).
|
|
29
|
+
4. If the entity is missing, say so and offer to create one via `remnic-remember` with the user's permission.
|
|
30
|
+
5. When multiple candidates match, list them briefly and ask which the user meant.
|
|
31
|
+
|
|
32
|
+
## Efficiency plan
|
|
33
|
+
|
|
34
|
+
- Do not call `remnic_entity_get` speculatively for every proper noun — only when the user asked or the task depends on it.
|
|
35
|
+
- Cache the entity payload within the current turn; do not re-fetch for the same name.
|
|
36
|
+
- Pair with `remnic-recall` when unstructured context would round out the entity view.
|
|
37
|
+
|
|
38
|
+
## Pitfalls and fixes
|
|
39
|
+
|
|
40
|
+
- **Pitfall:** Guessing entity names. **Fix:** Use the user's wording; disambiguate rather than inventing.
|
|
41
|
+
- **Pitfall:** Dumping the full entity payload. **Fix:** Summarize into a compact facts/relations/last-updated block.
|
|
42
|
+
- **Pitfall:** Treating a missing entity as a bug. **Fix:** Missing just means the graph has not captured it yet — offer to create it.
|
|
43
|
+
|
|
44
|
+
## Verification checklist
|
|
45
|
+
|
|
46
|
+
- [ ] `remnic_entity_get` was called with a user-supplied or user-confirmed name.
|
|
47
|
+
- [ ] Output shows facts, relationships, and timestamps in a compact view.
|
|
48
|
+
- [ ] Missing entities are acknowledged plainly with an offer to create.
|
|
49
|
+
- [ ] Legacy `engram_entity_get` alias was not preferred over `remnic_entity_get`.
|
|
50
|
+
|
|
51
|
+
> Tool names: canonical name is `remnic_entity_get`. The legacy `engram_entity_get` alias remains accepted during v1.x.
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: remnic-memory-workflow
|
|
3
|
+
description: Shared memory workflow for Remnic-connected agents — recall before acting, observe during work, remember at the end. Trigger phrases include "what do you remember about", "save this for later", "any context from last time".
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
allowed-tools:
|
|
6
|
+
- remnic_recall
|
|
7
|
+
- remnic_memory_store
|
|
8
|
+
- remnic_lcm_search
|
|
9
|
+
- remnic_entity_get
|
|
10
|
+
- remnic_observe
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## When to use
|
|
14
|
+
|
|
15
|
+
Use this skill as the default playbook whenever an agent picks up a task that could benefit from prior context, or when the user explicitly asks the agent to remember or recall something. It is the umbrella workflow — individual skills (`remnic-recall`, `remnic-remember`, `remnic-search`, `remnic-entities`, `remnic-status`) implement the detailed steps.
|
|
16
|
+
|
|
17
|
+
Triggers:
|
|
18
|
+
|
|
19
|
+
- The user opens a new task, ticket, or branch.
|
|
20
|
+
- The user says "what do you know about X", "have we talked about Y before", "remind me of Z".
|
|
21
|
+
- The user asks the agent to save a preference, decision, or fact.
|
|
22
|
+
- A long-running turn produces a durable outcome worth capturing.
|
|
23
|
+
|
|
24
|
+
## Inputs
|
|
25
|
+
|
|
26
|
+
- Current user request (natural language).
|
|
27
|
+
- Optional: active project path, ticket number, branch name.
|
|
28
|
+
- Optional: topic keywords surfaced earlier in the session.
|
|
29
|
+
|
|
30
|
+
## Procedure
|
|
31
|
+
|
|
32
|
+
1. **Recall first.** Call `remnic_recall` with a concise natural-language query derived from the user's request. Pull 3–8 results. Skim them for anything relevant.
|
|
33
|
+
2. **Mention relevant memories briefly** to the user if they materially change the plan, otherwise quietly use them as context.
|
|
34
|
+
3. **Observe during work.** For significant tool outputs (file edits, command exits, test results) call `remnic_observe` so Remnic can keep its ambient context fresh.
|
|
35
|
+
4. **Deep search when needed.** If `remnic_recall` misses something the user insists exists, call `remnic_lcm_search` with a more literal phrase.
|
|
36
|
+
5. **Browse entities.** When the user references a specific project, person, or concept by name, call `remnic_entity_get` to pull its facts and relationships.
|
|
37
|
+
6. **Remember at the end.** Before ending the turn, store any durable decision, preference, or finding with `remnic_memory_store`. Keep each entry under ~300 tokens.
|
|
38
|
+
|
|
39
|
+
## Efficiency plan
|
|
40
|
+
|
|
41
|
+
- Batch recalls. One broad query is usually cheaper than several narrow ones.
|
|
42
|
+
- Skip recall for trivially local requests (e.g., "format this JSON").
|
|
43
|
+
- Reuse the current turn's recall results instead of re-querying within the same turn.
|
|
44
|
+
- Store each memory once. If you are about to store something you just recalled, update instead of duplicating.
|
|
45
|
+
|
|
46
|
+
## Pitfalls and fixes
|
|
47
|
+
|
|
48
|
+
- **Pitfall:** Storing transient task state ("user is running tests now"). **Fix:** Only store facts with durable value — decisions, preferences, conclusions, long-lived context.
|
|
49
|
+
- **Pitfall:** Leaking secrets into memories. **Fix:** Redact API keys, tokens, and credentials before calling `remnic_memory_store`.
|
|
50
|
+
- **Pitfall:** Drowning the user in recalled context. **Fix:** Summarize in 1–3 bullet points; offer to expand on request.
|
|
51
|
+
- **Pitfall:** Forgetting the write. **Fix:** Make "remember the decision" the last step of any non-trivial turn.
|
|
52
|
+
|
|
53
|
+
## Verification checklist
|
|
54
|
+
|
|
55
|
+
- [ ] Recall was attempted before the agent committed to a plan.
|
|
56
|
+
- [ ] Any user-facing mention of recalled context was concise and attributed.
|
|
57
|
+
- [ ] Durable decisions, preferences, and findings were stored via `remnic_memory_store`.
|
|
58
|
+
- [ ] No secrets, credentials, or transient state were written.
|
|
59
|
+
- [ ] Legacy `engram_*` aliases were NOT preferred over canonical `remnic_*` tool names.
|
|
60
|
+
|
|
61
|
+
> Tool names: this skill uses the canonical `remnic_*` MCP tools. Legacy `engram_*` aliases (`engram_recall`, `engram_memory_store`, `engram_lcm_search`, `engram_entity_get`, `engram_observe`) remain accepted during v1.x for backward compatibility.
|