quilltap 4.10.0-dev.18 → 4.10.0-dev.184

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.
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ const { resolveServerPort } = require('./lock-helpers');
4
+
5
+ /**
6
+ * `quilltap anchor-probe <characterId>` — measure whether the episodic anchor
7
+ * line embedded with each memory suppresses reinforcement at admission.
8
+ *
9
+ * Thin wrapper over POST /api/v1/memories?action=anchor-gate-probe.
10
+ * Read-only; nothing is persisted server-side, but it costs embedding calls.
11
+ */
12
+
13
+ const RESET = '\x1b[0m';
14
+ const BOLD = '\x1b[1m';
15
+ const DIM = '\x1b[2m';
16
+ const GREEN = '\x1b[32m';
17
+ const RED = '\x1b[31m';
18
+ const YELLOW = '\x1b[33m';
19
+ const CYAN = '\x1b[36m';
20
+
21
+ function printAnchorProbeHelp() {
22
+ console.log(`
23
+ Quilltap anchor-probe Tool
24
+
25
+ Usage: quilltap anchor-probe <characterId> [options]
26
+
27
+ Measures whether the episodic anchor line embedded with each memory suppresses
28
+ reinforcement at admission. Re-embeds the character's most recent memories with
29
+ and without the anchor line, compares each against older rows, asks admission's
30
+ own decider what it would do each way, and reports how many rows would fold
31
+ into an older one (reinforce or better) and how many would be absorbed as
32
+ near-duplicates.
33
+
34
+ Read-only, but it costs embedding calls (roughly up to 7 per sampled row).
35
+
36
+ Options:
37
+ -d, --data-dir <path> Instance root; used to find the server's port
38
+ -i, --instance <name> Registered instance whose server to call (see 'quilltap instances')
39
+ --passphrase <pass> Accepted and ignored (this tool never opens the database)
40
+ --limit <number> Recent memories to sample (default: 50, max: 200)
41
+ --port <number> Server port for API calls. Default: the port recorded in the
42
+ --instance/--data-dir server's lock file, else 3000
43
+ --json Print the raw JSON result instead of tables
44
+ -h, --help Show this help
45
+
46
+ Examples:
47
+ quilltap anchor-probe <characterId>
48
+ quilltap anchor-probe <characterId> --limit 20
49
+ quilltap anchor-probe <characterId> --json > probe.json
50
+ `);
51
+ }
52
+
53
+ function parseFlags(args) {
54
+ const flags = { limit: undefined, port: undefined, dataDir: '', instance: '', json: false, help: false };
55
+ const positional = [];
56
+ let i = 0;
57
+ while (i < args.length) {
58
+ const a = args[i];
59
+ switch (a) {
60
+ case '--limit': {
61
+ const n = parseInt(args[++i], 10);
62
+ if (isNaN(n) || n < 1 || n > 200) {
63
+ console.error('Error: --limit must be between 1 and 200');
64
+ process.exit(1);
65
+ }
66
+ flags.limit = n;
67
+ break;
68
+ }
69
+ case '-d':
70
+ case '--data-dir':
71
+ flags.dataDir = args[++i];
72
+ break;
73
+ case '-i':
74
+ case '--instance':
75
+ flags.instance = args[++i];
76
+ break;
77
+ case '--passphrase':
78
+ // Accepted so universal flags never error; these tools talk to the
79
+ // running server and never open the database themselves.
80
+ i++;
81
+ break;
82
+ case '--port': {
83
+ const p = parseInt(args[++i], 10);
84
+ if (isNaN(p) || p < 1 || p > 65535) {
85
+ console.error('Error: --port must be between 1 and 65535');
86
+ process.exit(1);
87
+ }
88
+ flags.port = p;
89
+ break;
90
+ }
91
+ case '--json':
92
+ flags.json = true;
93
+ break;
94
+ case '-h':
95
+ case '--help':
96
+ flags.help = true;
97
+ break;
98
+ default:
99
+ if (a.startsWith('-')) {
100
+ console.error(`Unknown option: ${a}`);
101
+ process.exit(1);
102
+ }
103
+ positional.push(a);
104
+ }
105
+ i++;
106
+ }
107
+ return { flags, positional };
108
+ }
109
+
110
+ function fmt(n, digits = 3) {
111
+ if (n === null || n === undefined) return DIM + '—' + RESET;
112
+ return n.toFixed(digits);
113
+ }
114
+
115
+ // Pad a possibly colour-wrapped string to a visible width.
116
+ function pad(s, width) {
117
+ const visible = String(s).replace(/\x1b\[[0-9;]*m/g, '').length;
118
+ return String(s) + ' '.repeat(Math.max(0, width - visible));
119
+ }
120
+
121
+ function printRows(rows, thresholds) {
122
+ console.log(`\n${BOLD}Rows${RESET} (${rows.length})`);
123
+ if (rows.length === 0) {
124
+ console.log(` ${DIM}(none)${RESET}`);
125
+ return;
126
+ }
127
+ console.log(
128
+ ` ${DIM}${'anchored'.padEnd(10)}${'free'.padEnd(10)}${'delta'.padEnd(9)}${'anchor'.padEnd(8)}summary${RESET}`
129
+ );
130
+ const folds = (kind) => kind === 'reinforce' || kind === 'absorb' || kind === 'promote-and-reinforce';
131
+ for (const row of rows) {
132
+ const a = row.anchoredBest;
133
+ const f = row.anchorFreeBest;
134
+ const delta = typeof a === 'number' && typeof f === 'number' ? f - a : null;
135
+ const onlyFree = folds(row.anchorFreeAction) && !folds(row.anchoredAction);
136
+ const deltaStr = delta === null ? DIM + '—' + RESET : (delta >= 0 ? '+' : '') + delta.toFixed(3);
137
+ const anchorStr = row.hasAnchorLine ? 'yes' : `${DIM}no${RESET}`;
138
+ const summary = (row.summary || '').replace(/\s+/g, ' ').slice(0, 60);
139
+ const line = `${pad(fmt(a), 10)}${pad(fmt(f), 10)}${pad(deltaStr, 9)}${pad(anchorStr, 8)}${summary}`;
140
+ console.log(onlyFree ? `${YELLOW}▸ ${RESET}${line}` : ` ${line}`);
141
+ }
142
+ console.log(`\n ${YELLOW}▸${RESET} ${DIM}= would fold into an older row only without the anchor line (reinforce ≥ ${fmt(thresholds?.reinforce, 2)})${RESET}`);
143
+ }
144
+
145
+ async function anchorProbeCommand(args) {
146
+ const { flags, positional } = parseFlags(args);
147
+
148
+ if (flags.help || positional.length === 0) {
149
+ printAnchorProbeHelp();
150
+ process.exit(flags.help ? 0 : 1);
151
+ }
152
+ if (positional.length > 1) {
153
+ console.error('Error: only one characterId may be specified');
154
+ process.exit(1);
155
+ }
156
+ const characterId = positional[0];
157
+
158
+ let target;
159
+ try {
160
+ target = resolveServerPort({ port: flags.port, instance: flags.instance, dataDir: flags.dataDir });
161
+ } catch (err) {
162
+ console.error(`Error: ${err.message}`);
163
+ process.exit(1);
164
+ }
165
+ const port = target.port;
166
+ if (target.note) process.stderr.write(`${DIM}${target.note}${RESET}\n`);
167
+
168
+ const url = `http://localhost:${port}/api/v1/memories?action=anchor-gate-probe`;
169
+ const body = { characterId };
170
+ if (flags.limit !== undefined) body.limit = flags.limit;
171
+
172
+ process.stderr.write(`${BOLD}Probing anchor gate${RESET} for character ${DIM}${characterId}${RESET} via ${DIM}${url}${RESET}\n`);
173
+
174
+ let res;
175
+ try {
176
+ res = await fetch(url, {
177
+ method: 'POST',
178
+ headers: { 'content-type': 'application/json' },
179
+ body: JSON.stringify(body),
180
+ });
181
+ } catch (err) {
182
+ console.error(`${RED}Could not reach Quilltap server at http://localhost:${port}: ${err.message}${RESET}`);
183
+ console.error(target.hint);
184
+ process.exit(1);
185
+ }
186
+
187
+ let payload;
188
+ try {
189
+ payload = await res.json();
190
+ } catch {
191
+ console.error(`${RED}Server returned a non-JSON response (status ${res.status})${RESET}`);
192
+ process.exit(1);
193
+ }
194
+ if (!res.ok || payload?.success === false) {
195
+ console.error(`${RED}Probe failed (status ${res.status}): ${payload?.error || payload?.message || 'unknown error'}${RESET}`);
196
+ process.exit(1);
197
+ }
198
+
199
+ const result = payload.data ?? payload;
200
+ if (flags.json) {
201
+ console.log(JSON.stringify(result, null, 2));
202
+ return;
203
+ }
204
+
205
+ const t = result.thresholds || {};
206
+ const anchored = result.anchored || {};
207
+ const free = result.anchorFree || {};
208
+ console.log(`\n${BOLD}Character${RESET} ${result.characterId}`);
209
+ console.log(`${BOLD}Sampled${RESET} ${result.sampled} rows, ${result.anchoredRows} with an anchor line`);
210
+ console.log(`${BOLD}Embeddings${RESET} ${result.embeddingsGenerated} generated`);
211
+ if (result.note) console.log(`${YELLOW}${result.note}${RESET}`);
212
+ console.log(`\n${DIM}${''.padEnd(22)}${'anchored'.padEnd(11)}anchor-free${RESET}`);
213
+ console.log(`${BOLD}${'would fold'.padEnd(22)}${RESET}${String(anchored.reinforce).padEnd(11)}${CYAN}${free.reinforce}${RESET}`);
214
+ console.log(`${BOLD}${'near-dup absorbed'.padEnd(22)}${RESET}${String(anchored.nearDuplicate).padEnd(11)}${CYAN}${free.nearDuplicate}${RESET}`);
215
+ const only = result.crossedOnlyWithoutAnchors;
216
+ console.log(`${BOLD}Fold only without anchors${RESET} ${only > 0 ? YELLOW : GREEN}${only}${RESET}`);
217
+
218
+ printRows(result.rows || [], t);
219
+ console.log('');
220
+ }
221
+
222
+ module.exports = { anchorProbeCommand, printAnchorProbeHelp, parseFlags };
@@ -61,7 +61,7 @@ _quilltap_complete() {
61
61
  local global_opts="-d --data-dir -i --instance -p --port -o --open -v --version -h --help --update --passphrase"
62
62
 
63
63
  # Top-level subcommands
64
- local top_cmds="db docs themes instances memories memory-diff recall-replay logs migrations maintenance file-verify completion"
64
+ local top_cmds="db docs sync themes instances memories memory-diff recall-replay anchor-probe logs migrations maintenance file-verify completion"
65
65
 
66
66
  # Flags that swallow the word after them. A flat list will not do: -o is the
67
67
  # valueless global --open but themes' valued --output, and `memories`
@@ -69,13 +69,15 @@ _quilltap_complete() {
69
69
  # makes a flag's value look like the subcommand's verb, which is how
70
70
  # `quilltap docs --limit 5 <TAB>` used to lose the verb list.
71
71
  local vf_global=" -d --data-dir -i --instance -p --port --passphrase "
72
- local vf_db=" --limit --grep --character --project --about --source --chat --message --field --tail --last --from --type --out --id --count "
72
+ local vf_db=" --limit --grep --character --project --about --source --tier --chat --message --field --tail --last --from --type --out --id --count "
73
73
  local vf_docs=" --mount --folder --type --ext --limit --max --context --top --threshold --sort --depth --max-nodes --format "
74
+ local vf_sync=" --direction --prefer "
74
75
  local vf_themes=" -o --output -k --key -n --name "
75
- local vf_memories=" -d --data-dir --instance --passphrase --port --character --about --source --chat --project --since --until --min-importance --min-reinforced --sort --limit --in --max --context --depth --max-nodes --top --threshold "
76
+ local vf_memories=" -d --data-dir --instance --passphrase -p --port --phases --cap --character --about --source --tier --chat --project --since --until --min-importance --min-reinforced --sort --limit --in --max --context --depth --max-nodes --top --threshold "
76
77
  local vf_logs=" --stream --tail --grep "
77
78
  local vf_memory_diff=" --concurrency --out "
78
- local vf_recall_replay=" --turn --char --limit --port "
79
+ local vf_recall_replay=" --turn --char --limit --memory-budget --tuning --tuning-file --signals-from --port "
80
+ local vf_anchor_probe=" --limit --port "
79
81
  local vf_file_verify=" --stall-ms "
80
82
 
81
83
  # Get the subcommand (first non-option word after quilltap)
@@ -93,11 +95,13 @@ _quilltap_complete() {
93
95
  case "$subcommand" in
94
96
  db) valued="$vf_global$vf_db" ;;
95
97
  docs) valued="$vf_global$vf_docs" ;;
98
+ sync) valued="$vf_global$vf_sync" ;;
96
99
  themes) valued="$vf_global$vf_themes" ;;
97
100
  memories) valued="$vf_memories" ;;
98
101
  logs) valued="$vf_global$vf_logs" ;;
99
102
  memory-diff) valued="$vf_global$vf_memory_diff" ;;
100
103
  recall-replay) valued="$vf_global$vf_recall_replay" ;;
104
+ anchor-probe) valued="$vf_global$vf_anchor_probe" ;;
101
105
  file-verify) valued="$vf_global$vf_file_verify" ;;
102
106
  esac
103
107
 
@@ -174,7 +178,15 @@ _quilltap_complete() {
174
178
  return
175
179
  ;;
176
180
  --source)
177
- COMPREPLY=($(compgen -W "AUTO MANUAL" -- "$cur"))
181
+ COMPREPLY=($(compgen -W "AUTO MANUAL CONSOLIDATED" -- "$cur"))
182
+ return
183
+ ;;
184
+ --tier)
185
+ COMPREPLY=($(compgen -W "hot cold" -- "$cur"))
186
+ return
187
+ ;;
188
+ --phases)
189
+ COMPREPLY=($(compgen -W "fold-near-duplicates consolidate demote retire mirror" -- "$cur"))
178
190
  return
179
191
  ;;
180
192
  --sort)
@@ -197,6 +209,14 @@ _quilltap_complete() {
197
209
  COMPREPLY=($(compgen -W "args json" -- "$cur"))
198
210
  return
199
211
  ;;
212
+ --direction)
213
+ COMPREPLY=($(compgen -W "both to-disk to-store" -- "$cur"))
214
+ return
215
+ ;;
216
+ --prefer)
217
+ COMPREPLY=($(compgen -W "newer store disk" -- "$cur"))
218
+ return
219
+ ;;
200
220
  esac
201
221
 
202
222
  # Subcommand-specific completion
@@ -204,7 +224,7 @@ _quilltap_complete() {
204
224
  db)
205
225
  local db_verbs="schema find chats messages logs message log memories characters optimize backup integrity"
206
226
  local db_flags="--instance --data-dir --passphrase --json --limit --grep \
207
- --character --project --about --source --chat --message --rendered --field \
227
+ --character --project --about --source --tier --chat --message --rendered --field \
208
228
  --tail --last --full --from --type --out --id --diverged --blocked --help \
209
229
  --tables --count --repl --write --llm-logs --mount-points \
210
230
  --lock-status --lock-clean --lock-override"
@@ -241,6 +261,18 @@ _quilltap_complete() {
241
261
  _quilltap_docs_positional "$subverb" "$positional_count" "$cur"
242
262
  fi
243
263
  ;;
264
+ sync)
265
+ local sync_flags="--dry-run --direction --prefer --no-delete --no-manifest \
266
+ --json --instance --data-dir --passphrase --port --help"
267
+ if [[ "$cur" == -* ]]; then
268
+ COMPREPLY=($(compgen -W "$sync_flags" -- "$cur"))
269
+ elif [[ "$positional_count" == "1" ]]; then
270
+ _quilltap_lines_compreply "$cur" \
271
+ <<< "$(command quilltap docs list --names-only "${ctx_flags[@]}" 2>/dev/null)"
272
+ else
273
+ COMPREPLY=($(compgen -d -- "$cur"))
274
+ fi
275
+ ;;
244
276
  themes)
245
277
  local themes_verbs="list install uninstall validate export create search update registry"
246
278
  local themes_flags="--instance --data-dir --output -o --help"
@@ -289,13 +321,13 @@ _quilltap_complete() {
289
321
  fi
290
322
  ;;
291
323
  memories)
292
- local mem_verbs="ls find grep show tree status validate"
293
- local mem_flags="--character --about --source --chat --project --since --until \
324
+ local mem_verbs="ls find grep show tree status validate maintain"
325
+ local mem_flags="--character --about --source --tier --chat --project --since --until \
294
326
  --min-importance --min-reinforced --has-embedding --no-embedding \
295
327
  --sort -r --reverse --limit --full-titles --in --no-related --list \
296
328
  --ignore-case -i --paths-only -l --max --context --depth --max-nodes \
297
- --semantic --top --threshold \
298
- --instance --data-dir --passphrase --port --json --help"
329
+ --semantic --top --threshold --dry-run --phases --cap --repair --write \
330
+ --instance --data-dir --passphrase -p --port --json --help"
299
331
  if [[ -z "$subverb" ]]; then
300
332
  if [[ "$cur" == -* ]]; then
301
333
  COMPREPLY=($(compgen -W "$mem_flags" -- "$cur"))
@@ -311,9 +343,13 @@ _quilltap_complete() {
311
343
  COMPREPLY=($(compgen -W "$md_flags" -- "$cur"))
312
344
  ;;
313
345
  recall-replay)
314
- local rr_flags="--turn --char --limit --port --json --help"
346
+ local rr_flags="--instance --data-dir --passphrase --turn --char --limit --memory-budget --tuning --tuning-file --signals-from --as-of --port --json --help"
315
347
  COMPREPLY=($(compgen -W "$rr_flags" -- "$cur"))
316
348
  ;;
349
+ anchor-probe)
350
+ local ap_flags="--instance --data-dir --passphrase --limit --port --json --help"
351
+ COMPREPLY=($(compgen -W "$ap_flags" -- "$cur"))
352
+ ;;
317
353
  logs)
318
354
  local logs_flags="--stream --tail -f --follow --grep \
319
355
  --instance --data-dir --passphrase --help"
@@ -19,7 +19,7 @@ function __quilltap_no_subcommand
19
19
  end
20
20
  for i in (seq 2 (count $cmd))
21
21
  switch $cmd[$i]
22
- case db docs themes instances memories memory-diff recall-replay logs migrations maintenance file-verify completion
22
+ case db docs sync themes instances memories memory-diff recall-replay anchor-probe logs migrations maintenance file-verify completion
23
23
  return 1
24
24
  end
25
25
  end
@@ -31,7 +31,7 @@ function __quilltap_using_subcommand
31
31
  set -l cmd (commandline -opc)
32
32
  for i in (seq 2 (count $cmd))
33
33
  switch $cmd[$i]
34
- case db docs themes instances memories memory-diff recall-replay logs migrations maintenance file-verify completion
34
+ case db docs sync themes instances memories memory-diff recall-replay anchor-probe logs migrations maintenance file-verify completion
35
35
  test "$cmd[$i]" = "$target"
36
36
  return $status
37
37
  end
@@ -55,11 +55,13 @@ end
55
55
 
56
56
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'db' -d 'Query encrypted databases'
57
57
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'docs' -d 'Inspect and read document mounts'
58
+ complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'sync' -d 'Mirror a database-backed store to a directory'
58
59
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'themes' -d 'Manage theme bundles'
59
60
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'instances' -d 'Register or inspect instances'
60
61
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'memories' -d 'Search and browse memories'
61
62
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'memory-diff' -d 'Memory extraction dry-run'
62
63
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'recall-replay' -d 'Replay memory recall for a turn'
64
+ complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'anchor-probe' -d 'Probe anchor-line effect on admission'
63
65
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'logs' -d 'Tail or print log files'
64
66
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'migrations' -d 'Inspect migration status'
65
67
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'maintenance' -d 'Run retention/cleanup sweeps'
@@ -111,7 +113,8 @@ complete -c quilltap -n '__quilltap_using_subcommand db' -l 'grep' -d 'Substring
111
113
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'character' -d 'Character name or id' -x
112
114
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'project' -d 'Project name or id' -x
113
115
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'about' -d 'Subject character' -x
114
- complete -c quilltap -n '__quilltap_using_subcommand db' -l 'source' -d 'Memory source' -x -a 'AUTO MANUAL'
116
+ complete -c quilltap -n '__quilltap_using_subcommand db' -l 'source' -d 'Memory source' -x -a 'AUTO MANUAL CONSOLIDATED'
117
+ complete -c quilltap -n '__quilltap_using_subcommand db' -l 'tier' -d 'Memory tier' -x -a 'hot cold'
115
118
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'chat' -d 'Chat name or id' -x
116
119
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'message' -d 'Message id' -x
117
120
  complete -c quilltap -n '__quilltap_using_subcommand db' -l 'rendered' -d 'Render rich content'
@@ -188,6 +191,22 @@ complete -c quilltap -n '__quilltap_using_subcommand docs' -l 'uri' -d 'Show can
188
191
  complete -c quilltap -n '__quilltap_using_subcommand docs' -l 'base64' -d 'Base64 transfer for binary files'
189
192
  complete -c quilltap -n '__quilltap_using_subverb docs docker-mounts' -l 'format' -d 'Output shape' -x -a 'args json'
190
193
 
194
+ # ---------- sync ----------
195
+ # `quilltap sync <store> <path>`: the first positional is a store, the second a
196
+ # local directory — so the verb is not -f (file completion stays available).
197
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -f -a '(__quilltap_mount_names)' -d 'Document store'
198
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'dry-run' -d 'Plan and print; change nothing'
199
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'direction' -d 'Which side may change' -x -a 'both to-disk to-store'
200
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'prefer' -d 'How to resolve a conflict' -x -a 'newer store disk'
201
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'no-delete' -d 'Never propagate a deletion'
202
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'no-manifest' -d 'Ignore .quilltap-sync.json'
203
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'json' -d 'Machine-readable plan and results'
204
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'port' -s 'p' -d 'Server port' -x
205
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'instance' -s 'i' -d 'Registered instance' -x -a '(__quilltap_instance_names)'
206
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'data-dir' -s 'd' -d 'Data directory' -x -a '(__fish_complete_directories)'
207
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'passphrase' -d 'Database passphrase' -x
208
+ complete -c quilltap -n '__quilltap_using_subcommand sync' -l 'help' -s 'h' -d 'Show help'
209
+
191
210
  # ---------- themes verbs ----------
192
211
  complete -c quilltap -n '__quilltap_using_subcommand themes' -f -a 'list' -d 'List themes'
193
212
  complete -c quilltap -n '__quilltap_using_subcommand themes' -f -a 'install' -d 'Install theme'
@@ -254,11 +273,18 @@ complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'show' -d '
254
273
  complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'tree' -d 'Graph walk'
255
274
  complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'status' -d 'Memory status'
256
275
  complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'validate' -d 'Health check'
276
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -f -a 'maintain' -d 'Run memory maintenance'
257
277
 
258
278
  # memories flags
259
279
  complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'character' -d 'Character name or id' -x
260
280
  complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'about' -d 'Subject of memory' -x
261
- complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'source' -d 'Memory source' -x -a 'AUTO MANUAL'
281
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'source' -d 'Memory source' -x -a 'AUTO MANUAL CONSOLIDATED'
282
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'tier' -d 'Memory tier' -x -a 'hot cold'
283
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'dry-run' -d 'Maintain: rehearse and print the report, write nothing'
284
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'phases' -d 'Maintain: comma-separated phases' -x -a 'fold-near-duplicates consolidate demote retire mirror'
285
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'cap' -d 'Maintain: hot-row cap for demotion' -x
286
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'repair' -d 'Validate: repair the graph on the running server'
287
+ complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'write' -d 'Validate --repair: apply the repair'
262
288
  complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'chat' -d 'Chat id or title' -x
263
289
  complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'project' -d 'Project name or id' -x
264
290
  complete -c quilltap -n '__quilltap_using_subcommand memories' -l 'since' -d 'Since timestamp' -x
@@ -294,8 +320,18 @@ complete -c quilltap -n '__quilltap_using_subcommand memory-diff' -l 'out' -d 'O
294
320
  complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'turn' -d 'Interchange to replay (1-based)' -x
295
321
  complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'char' -d 'Character whose memories are searched' -x
296
322
  complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'limit' -d 'Candidate rows per path' -x
323
+ complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'memory-budget' -d 'Memory token budget' -x
324
+ complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'tuning' -d 'New-path tuning JSON' -x
325
+ complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'tuning-file' -d 'New-path tuning JSON file' -r -F
326
+ complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'signals-from' -d 'Reuse signals from a saved replay' -r -F
327
+ complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'as-of' -d 'Search the corpus as it stood at the turn'
297
328
  complete -c quilltap -n '__quilltap_using_subcommand recall-replay' -l 'port' -d 'Server port' -x
298
329
 
330
+ # ---------- anchor-probe flags ----------
331
+ complete -c quilltap -n '__quilltap_using_subcommand anchor-probe' -l 'limit' -d 'Recent memories to sample' -x
332
+ complete -c quilltap -n '__quilltap_using_subcommand anchor-probe' -l 'port' -d 'Server port' -x
333
+ complete -c quilltap -n '__quilltap_using_subcommand anchor-probe' -l 'json' -d 'Raw JSON output'
334
+
299
335
  # ---------- file-verify flags ----------
300
336
  complete -c quilltap -n '__quilltap_using_subcommand file-verify' -l 'all' -d 'Read every top-level file, not just dataless ones'
301
337
  complete -c quilltap -n '__quilltap_using_subcommand file-verify' -l 'stall-ms' -d 'Per-chunk stall threshold (ms)' -x
@@ -37,11 +37,13 @@ _quilltap() {
37
37
  subcommands=(
38
38
  'db:Query encrypted databases'
39
39
  'docs:Inspect, read, and export document mounts'
40
+ 'sync:Mirror a database-backed store to a directory'
40
41
  'themes:Manage theme bundles'
41
42
  'instances:Register or inspect named Quilltap instances'
42
43
  'memories:Search, browse, and graph memories'
43
44
  'memory-diff:Dump existing memories and dry-run re-extraction'
44
45
  'recall-replay:Replay a turn'\''s memory recall, old vs episodic ranking'
46
+ 'anchor-probe:Probe whether episodic anchor lines suppress reinforcement at admission'
45
47
  'logs:Tail or print an instance log file'
46
48
  'migrations:Inspect migration status'
47
49
  'maintenance:Run retention/cleanup sweeps'
@@ -80,6 +82,9 @@ _quilltap_subcommand() {
80
82
  docs)
81
83
  _quilltap_docs
82
84
  ;;
85
+ sync)
86
+ _quilltap_sync
87
+ ;;
83
88
  themes)
84
89
  _quilltap_themes
85
90
  ;;
@@ -95,6 +100,9 @@ _quilltap_subcommand() {
95
100
  recall-replay)
96
101
  _quilltap_recall_replay
97
102
  ;;
103
+ anchor-probe)
104
+ _quilltap_anchor_probe
105
+ ;;
98
106
  logs)
99
107
  _quilltap_logs
100
108
  ;;
@@ -143,7 +151,8 @@ _quilltap_db() {
143
151
  '--character[Character name or id]:character:'
144
152
  '--project[Project name or id]:project:'
145
153
  '--about[Subject character]:character:'
146
- '--source[Memory source]:source:(AUTO MANUAL)'
154
+ '--source[Memory source]:source:(AUTO MANUAL CONSOLIDATED)'
155
+ '--tier[Memory tier]:tier:(hot cold)'
147
156
  '--chat[Chat name or id]:chat:'
148
157
  '--message[Message id]:id:'
149
158
  '--rendered[Render rich content]'
@@ -314,6 +323,38 @@ _quilltap_docs() {
314
323
  esac
315
324
  }
316
325
 
326
+ # `quilltap sync <store> <path>` — a store, then a local directory.
327
+ _quilltap_sync() {
328
+ local -a sync_opts
329
+ sync_opts=(
330
+ '(-i --instance)'{-i,--instance}'[Registered instance name]:instance:_quilltap_instance_names'
331
+ '(-d --data-dir)'{-d,--data-dir}'[Data directory]:directory:_directories'
332
+ '--passphrase[Database passphrase]:passphrase:'
333
+ '(-p --port)'{-p,--port}'[Server port]:port:'
334
+ '--json[Machine-readable plan and results]'
335
+ '--dry-run[Plan and print; change nothing]'
336
+ '--direction[Which side may change]:direction:(both to-disk to-store)'
337
+ '--prefer[How to resolve a conflict]:prefer:(newer store disk)'
338
+ '--no-delete[Never propagate a deletion]'
339
+ '--no-manifest[Ignore .quilltap-sync.json]'
340
+ '(-h --help)'{-h,--help}'[Show help]'
341
+ )
342
+
343
+ _arguments -C $sync_opts \
344
+ '1: :->store' \
345
+ '2: :->target' \
346
+ '*: :'
347
+
348
+ case "$state" in
349
+ store)
350
+ _quilltap_mount_names
351
+ ;;
352
+ target)
353
+ _directories
354
+ ;;
355
+ esac
356
+ }
357
+
317
358
  # Which docs positionals name a document store, and which name a local path.
318
359
  # `move`/`copy`/`link` take <srcMount> <srcPath> <dstMount> <dstPath>, so a
319
360
  # store is wanted at both 2 and 4; everything else that takes a store takes it
@@ -498,17 +539,24 @@ _quilltap_memories() {
498
539
  'tree:ASCII walk of graph'
499
540
  'status:Per-character rollup'
500
541
  'validate:Health check'
542
+ 'maintain:Run memory maintenance (fold, consolidate, demote, retire, mirror)'
501
543
  )
502
544
 
503
545
  mem_opts=(
504
546
  '--instance[Registered instance name]:instance:_quilltap_instance_names'
505
547
  '(-d --data-dir)'{-d,--data-dir}'[Data directory]:directory:_directories'
506
548
  '--passphrase[Database passphrase]:passphrase:'
507
- '--port[Server port]:port:'
549
+ '(-p --port)'{-p,--port}'[Server port]:port:'
508
550
  '--json[JSON output]'
509
551
  '--character[Character name or id]:character:'
510
552
  '--about[Subject of memory]:character:'
511
- '--source[Memory source]:source:(AUTO MANUAL)'
553
+ '--source[Memory source]:source:(AUTO MANUAL CONSOLIDATED)'
554
+ '--tier[Memory tier]:tier:(hot cold)'
555
+ '--dry-run[Maintain: rehearse and print the report, write nothing]'
556
+ '--phases[Maintain: comma-separated phases]:phases:_sequence compadd - fold-near-duplicates consolidate demote retire mirror'
557
+ '--cap[Maintain: hot-row cap for demotion]:n:'
558
+ '--repair[Validate: repair the graph on the running server (dry run without --write)]'
559
+ '--write[Validate --repair: apply the repair]'
512
560
  '--chat[Chat id or title]:chat:'
513
561
  '--project[Project name or id]:project:'
514
562
  '--since[Since timestamp]:date:'
@@ -568,6 +616,23 @@ _quilltap_recall_replay() {
568
616
  '--turn[interchange to replay]:turn:' \
569
617
  '--char[character id]:char:' \
570
618
  '--limit[rows per path]:limit:' \
619
+ '--memory-budget[memory token budget]:tokens:' \
620
+ '--tuning[new-path tuning JSON]:json:' \
621
+ '--tuning-file[new-path tuning JSON file]:file:_files' \
622
+ '--signals-from[reuse signals from a saved replay]:file:_files' \
623
+ '--as-of[search the corpus as it stood at the turn]' \
624
+ '--port[server port]:port:' \
625
+ '--json[raw JSON output]' \
626
+ '*: :'
627
+ }
628
+
629
+ _quilltap_anchor_probe() {
630
+ _arguments \
631
+ '(-h --help)'{-h,--help}'[Show help]' \
632
+ '(-i --instance)'{-i,--instance}'[Use instance]:instance:_quilltap_instance_names' \
633
+ '(-d --data-dir)'{-d,--data-dir}'[Data directory]:directory:_directories' \
634
+ '--passphrase[Database passphrase]:passphrase:' \
635
+ '--limit[recent memories to sample]:limit:' \
571
636
  '--port[server port]:port:' \
572
637
  '--json[raw JSON output]' \
573
638
  '*: :'