quilltap 4.10.0-dev.18 → 4.10.0-dev.183
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 +183 -15
- package/bin/quilltap.js +61 -26
- package/lib/__tests__/completion-coverage.test.js +2 -0
- package/lib/__tests__/memories-maintain-command.test.js +276 -0
- package/lib/__tests__/migrations-source-ids.test.js +54 -0
- package/lib/__tests__/server-port-resolution.test.js +122 -0
- package/lib/__tests__/sync-report.test.js +158 -0
- package/lib/anchor-probe-command.js +222 -0
- package/lib/completion/bash.template +47 -11
- package/lib/completion/fish.template +40 -4
- package/lib/completion/zsh.template +68 -3
- package/lib/db-commands.js +39 -3
- package/lib/db-helpers.js +12 -0
- package/lib/graph-integrity.js +108 -5
- package/lib/lock-helpers.js +66 -0
- package/lib/memories-commands.js +396 -76
- package/lib/memories-maintain-command.js +238 -0
- package/lib/memories-server.js +86 -0
- package/lib/memory-diff-command.js +28 -6
- package/lib/migrations-commands.js +18 -4
- package/lib/native-modules.js +102 -1
- package/lib/recall-replay-command.js +193 -11
- package/lib/sync-command.js +323 -0
- package/lib/sync-report.js +167 -0
- package/lib/text-codec.js +90 -0
- package/package.json +1 -1
|
@@ -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
|
'*: :'
|