@jhizzard/termdeck 0.6.0 → 0.6.2
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/README.md +3 -3
- package/package.json +1 -1
- package/packages/cli/src/init-mnestra.js +13 -10
- package/packages/server/src/setup/prompts.js +87 -14
package/README.md
CHANGED
|
@@ -81,7 +81,7 @@ What's excluded at this tier: **Flashback is silent**, the "Ask about this termi
|
|
|
81
81
|
|
|
82
82
|
### Tier 2 — Add Mnestra to light up Flashback
|
|
83
83
|
|
|
84
|
-
Mnestra is a separate npm package — `@jhizzard/mnestra@0.2.
|
|
84
|
+
Mnestra is a separate npm package — `@jhizzard/mnestra@0.2.1` — that ships a Postgres-backed persistent memory store with an MCP server, a webhook server, six search tools, and six SQL migrations. It can be consumed by TermDeck (for Flashback), by Claude Code (as an MCP memory layer), or by any tool that speaks the MCP protocol.
|
|
85
85
|
|
|
86
86
|
To enable Flashback in TermDeck:
|
|
87
87
|
|
|
@@ -147,7 +147,7 @@ Restart Claude Code. Six MCP tools appear: `memory_remember`, `memory_recall`, `
|
|
|
147
147
|
|
|
148
148
|
### Tier 3 — Add Rumen for async learning
|
|
149
149
|
|
|
150
|
-
Rumen is a separate npm package — `@jhizzard/rumen@0.4.
|
|
150
|
+
Rumen is a separate npm package — `@jhizzard/rumen@0.4.3` — that ships as a Supabase Edge Function designed to run on a 15-minute `pg_cron` schedule. It's the async reflection layer over Mnestra: it reads recent session memories, cross-references them with your entire historical corpus via hybrid search, synthesizes insights via Claude Haiku, and writes the results back into `rumen_insights` (a new table alongside Mnestra's `memory_items`). TermDeck's Flashback and Claude Code's `memory_recall` both automatically benefit because insights flow back into the same database.
|
|
151
151
|
|
|
152
152
|
**Rumen is live.** First full-kickstart run against a production Mnestra store on 2026-04-15 19:47 UTC: **111 sessions processed, 111 insights generated** in one pass. Insights surfaced patterns like "the error detection regex in Flashback misses `No such file or directory` — same class of blind spot as X" and "Practice sessions exist as a separate model but frontend components were built and never wired into the schedule view." The cognitive loop is closed.
|
|
153
153
|
|
|
@@ -171,7 +171,7 @@ Honest limits, stated upfront so the skeptic has nothing to chase:
|
|
|
171
171
|
- **Not a replacement for reading docs.** It's the shortest path to a memory you already wrote. If the memory isn't there, the feature does nothing.
|
|
172
172
|
- **Not fully local by default.** Tier 2+ reaches out to Supabase for storage and OpenAI for embeddings. Tier 1 is fully local. A fully-local Tier 2 (local Postgres + local embeddings) is on the roadmap.
|
|
173
173
|
- **Not free forever.** Tier 2+ pays OpenAI fractions of a cent per memory for embeddings. Self-hosted embeddings via Ollama are on the roadmap.
|
|
174
|
-
- **Not proven at scale.** v0.
|
|
174
|
+
- **Not proven at scale.** v0.6.1, validated against 4,590 memories in one developer's production store. The Rumen 2026-04-19 re-kickstart processed 166 sessions into 166 insights in ~5.5 minutes. No multi-user data yet. Bug reports and issues welcome.
|
|
175
175
|
|
|
176
176
|
---
|
|
177
177
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jhizzard/termdeck",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Browser-based terminal multiplexer with metadata overlays, panel flashback memory recall, and AI-aware session management",
|
|
5
5
|
"bin": {
|
|
6
6
|
"termdeck": "./packages/cli/src/index.js"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
//
|
|
14
14
|
// Flags:
|
|
15
15
|
// --help Print usage and exit
|
|
16
|
-
// --yes
|
|
16
|
+
// --yes Reserved (no-op as of v0.6.2 — wizard no longer asks "Proceed?" after secrets)
|
|
17
17
|
// --dry-run Print what the wizard would do; don't touch the DB or filesystem
|
|
18
18
|
// --skip-verify Skip the final memory_status_aggregation() check
|
|
19
19
|
//
|
|
@@ -41,7 +41,7 @@ const HELP = [
|
|
|
41
41
|
'',
|
|
42
42
|
'Flags:',
|
|
43
43
|
' --help Print this message and exit',
|
|
44
|
-
' --yes
|
|
44
|
+
' --yes Reserved (no-op as of v0.6.2 — kept for forward compatibility)',
|
|
45
45
|
' --dry-run Print the plan without touching the database or filesystem',
|
|
46
46
|
' --skip-verify Skip the final memory_status_aggregation() sanity call',
|
|
47
47
|
'',
|
|
@@ -129,14 +129,17 @@ async function collectInputs({ yes }) {
|
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
132
|
+
// No confirm here — the user already opted in by typing
|
|
133
|
+
// `termdeck init --mnestra` and supplying every secret. The previous
|
|
134
|
+
// confirm gate was the consistent failure point in Brad's reports
|
|
135
|
+
// (2026-04-25 twice + a third report after v0.6.1) on terminals that
|
|
136
|
+
// emit stray bytes (CRLF, ANSI cursor reports, paste-bracketing) which
|
|
137
|
+
// contaminated readline and made the confirm fast-resolve to "no" or
|
|
138
|
+
// an empty cancel. Migrations are `IF NOT EXISTS` so a re-run is safe;
|
|
139
|
+
// Ctrl-C still aborts cleanly. The `--yes` flag is preserved as a
|
|
140
|
+
// stable CLI surface for callers/scripts and for future use.
|
|
141
|
+
void yes;
|
|
142
|
+
process.stdout.write('\n');
|
|
140
143
|
|
|
141
144
|
return {
|
|
142
145
|
projectUrl,
|
|
@@ -105,13 +105,38 @@ async function askOptional(question, { validate } = {}) {
|
|
|
105
105
|
// Secret prompt. On TTY we mute echo; on non-TTY we fall back to a visible
|
|
106
106
|
// line read. Callers typically pass an empty string for `question` and write
|
|
107
107
|
// their own label first.
|
|
108
|
+
//
|
|
109
|
+
// Reported as broken on MobaXterm SSH (Brad, 2026-04-25): the wizard would
|
|
110
|
+
// abort after the Anthropic key prompt as if Ctrl-C were pressed. Three real
|
|
111
|
+
// bugs hardened against here, all from the original raw-mode loop:
|
|
112
|
+
//
|
|
113
|
+
// 1. CRLF leak. When the terminal sends "\r\n" as a single chunk (Windows /
|
|
114
|
+
// MobaXterm Enter key), the original loop matched the "\r", resolved,
|
|
115
|
+
// and dropped the rest of the chunk on the floor. The trailing "\n"
|
|
116
|
+
// then surfaced through the next prompt's data path. Worst case the
|
|
117
|
+
// dropped chunk also contained from a stray keystroke and the
|
|
118
|
+
// original SIGINT branch fired, killing the wizard mid-flow.
|
|
119
|
+
//
|
|
120
|
+
// 2. ANSI / escape-sequence pollution. Some terminals emit "[..."
|
|
121
|
+
// sequences for non-character events (focus changes, cursor reports,
|
|
122
|
+
// paste-bracketing). The old loop fed those bytes into the password
|
|
123
|
+
// buffer and echoed "*" for each one. We now consume escape sequences
|
|
124
|
+
// silently.
|
|
125
|
+
//
|
|
126
|
+
// 3. SIGINT during a secret prompt is now a soft cancel — return empty
|
|
127
|
+
// string, let the caller's shape validator re-prompt — instead of
|
|
128
|
+
// `process.kill`-ing the process. The hard-kill was masking the CRLF
|
|
129
|
+
// bug above and aborting the wizard from stray bytes.
|
|
130
|
+
//
|
|
131
|
+
// Carry-over bytes (anything in the chunk after the resolving "\r"/"\n")
|
|
132
|
+
// are pushed back onto stdin via `stdin.unshift` so the next consumer
|
|
133
|
+
// (readline, the next askSecret) reads from a clean stream. Regression
|
|
134
|
+
// fixtures in tests/setup-prompts.test.js.
|
|
108
135
|
async function askSecret(question) {
|
|
109
136
|
if (!process.stdin.isTTY) {
|
|
110
137
|
return ask(question);
|
|
111
138
|
}
|
|
112
139
|
if (question) process.stdout.write(`${question}: `);
|
|
113
|
-
// Raw-mode reader. Detach the shared readline for the duration so both
|
|
114
|
-
// consumers aren't racing on stdin 'data' events.
|
|
115
140
|
return new Promise((resolve) => {
|
|
116
141
|
if (rl) rl.pause();
|
|
117
142
|
const stdin = process.stdin;
|
|
@@ -120,30 +145,78 @@ async function askSecret(question) {
|
|
|
120
145
|
stdin.setEncoding('utf-8');
|
|
121
146
|
|
|
122
147
|
let buffer = '';
|
|
148
|
+
let inEscape = false;
|
|
149
|
+
let escapeDepth = 0;
|
|
150
|
+
|
|
151
|
+
const cleanup = () => {
|
|
152
|
+
stdin.setRawMode(false);
|
|
153
|
+
stdin.removeListener('data', onData);
|
|
154
|
+
process.stdout.write('\n');
|
|
155
|
+
if (rl) rl.resume();
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const carryOver = (chunk, fromIndex) => {
|
|
159
|
+
if (fromIndex >= chunk.length) return;
|
|
160
|
+
const tail = chunk.slice(fromIndex);
|
|
161
|
+
// Drop a single leading \n if we just consumed \r (CRLF pair already
|
|
162
|
+
// accounted for at the call site).
|
|
163
|
+
const trimmed = tail[0] === '\n' ? tail.slice(1) : tail;
|
|
164
|
+
if (trimmed.length > 0) {
|
|
165
|
+
try { stdin.unshift(Buffer.from(trimmed, 'utf-8')); } catch (_e) { /* unsupported on some Node versions */ }
|
|
166
|
+
}
|
|
167
|
+
};
|
|
168
|
+
|
|
123
169
|
const onData = (chunk) => {
|
|
124
|
-
for (
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
170
|
+
for (let i = 0; i < chunk.length; i++) {
|
|
171
|
+
const ch = chunk[i];
|
|
172
|
+
|
|
173
|
+
// Bug #2: silently consume ANSI escape sequences. ESC (\u001b) starts one;
|
|
174
|
+
// we consume until a final byte (CSI: 0x40..0x7E) or run out of
|
|
175
|
+
// patience (16 bytes).
|
|
176
|
+
if (inEscape) {
|
|
177
|
+
escapeDepth++;
|
|
178
|
+
if ((escapeDepth === 1 && ch !== '[') ||
|
|
179
|
+
(escapeDepth > 1 && ch >= '@' && ch <= '~') ||
|
|
180
|
+
escapeDepth > 16) {
|
|
181
|
+
inEscape = false;
|
|
182
|
+
escapeDepth = 0;
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
if (ch === '') { inEscape = true; escapeDepth = 0; continue; }
|
|
187
|
+
|
|
188
|
+
// Line terminators — the resolve path. Bug #1 handled here via the
|
|
189
|
+
// explicit CRLF drain inside the same chunk.
|
|
190
|
+
if (ch === '\n' || ch === '\r' || ch === '') {
|
|
191
|
+
let consumeUpTo = i + 1;
|
|
192
|
+
if (ch === '\r' && chunk[i + 1] === '\n') consumeUpTo++;
|
|
193
|
+
cleanup();
|
|
194
|
+
carryOver(chunk, consumeUpTo);
|
|
130
195
|
resolve(buffer);
|
|
131
196
|
return;
|
|
132
197
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
198
|
+
|
|
199
|
+
// Bug #3: Ctrl-C during a secret prompt → soft cancel, not SIGINT.
|
|
200
|
+
if (ch === '') {
|
|
201
|
+
cleanup();
|
|
202
|
+
carryOver(chunk, i + 1);
|
|
203
|
+
resolve('');
|
|
138
204
|
return;
|
|
139
205
|
}
|
|
140
|
-
|
|
206
|
+
|
|
207
|
+
// Backspace / DEL.
|
|
208
|
+
if (ch === '' || ch === '\b') {
|
|
141
209
|
if (buffer.length > 0) {
|
|
142
210
|
buffer = buffer.slice(0, -1);
|
|
143
211
|
process.stdout.write('\b \b');
|
|
144
212
|
}
|
|
145
213
|
continue;
|
|
146
214
|
}
|
|
215
|
+
|
|
216
|
+
// Drop any other control character silently; never let them into
|
|
217
|
+
// the password buffer.
|
|
218
|
+
if (ch < ' ' && ch !== '\t') continue;
|
|
219
|
+
|
|
147
220
|
buffer += ch;
|
|
148
221
|
process.stdout.write('*');
|
|
149
222
|
}
|