quilltap 4.10.0-dev.175 → 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 CHANGED
@@ -234,7 +234,8 @@ quilltap docs list # All mounts
234
234
  quilltap docs show <mount> # One mount, with counts
235
235
  quilltap docs files <mount> [--folder <path>] # Flat file list for a mount
236
236
  quilltap docs ls <mount> [path] [--links] # POSIX-flavoured listing (alias: dir)
237
- quilltap docs tree <mount> [path] # ASCII tree of a folder hierarchy (--depth, --max-nodes)
237
+ quilltap docs ls <mount> [path] -R --sort time -r # Recursive, grouped by folder, sorted by time and reversed
238
+ quilltap docs tree <mount> [path] # ASCII tree of a folder hierarchy (--depth, --max-nodes, --long)
238
239
  quilltap docs read [--rendered] <mount> <path> # File contents → stdout
239
240
  quilltap docs export <mount> <outputDir> # Mount → directory
240
241
  quilltap docs find <pattern> # Substring match on file names (--mount, --ext, --type, --limit)
@@ -258,6 +259,8 @@ quilltap docs rmdir <mount> <path> # Delete an empty
258
259
  quilltap docs mvdir <mount> <fromPath> <toPath> # Rename/move a folder (server-required)
259
260
  ```
260
261
 
262
+ `ls` flags: `--recursive` / `-R` lists every file below the path, grouped by folder; `--sort name|time|size|links` (default `name`) with `--reverse` / `-r` to flip it; `--links` lists the other mount/path entries under each file that has more than one hard link. `tree` takes `--depth N` (default 20), `--max-nodes N` (default 1000) and `--long`, which is accepted but reserved — it does not yet add columns.
263
+
261
264
  Mount arguments accept the mount name (case-insensitive) or a UUID; ambiguous names print candidates and exit non-zero. `--json` is supported by every verb; `reindex`, `embed`, `link`, `rmdir`, and `mvdir` refuse to run without a reachable server. `grep --semantic` goes through `POST /api/v1/mount-points?action=semantic-search`, because the embedding provider lives in the server; it defaults to `--top 20`, `--threshold 0.5`, `--port 3000`.
262
265
 
263
266
  ### Addressing documents with `qtap://` URIs
@@ -337,6 +340,7 @@ quilltap memories ls # All hol
337
340
  quilltap memories ls --character Ariadne --sort created --limit 10 # One holder, newest first
338
341
  quilltap memories find "concrete examples" # Substring match on summary (--in content|both)
339
342
  quilltap memories grep -i --max 3 --context 1 "concrete examples" # Pattern search inside content, with snippets
343
+ quilltap memories grep -l "concrete examples" # Matching memory IDs only, one per line (--paths-only)
340
344
  quilltap memories show <id|prefix> [--depth N] [--no-related] # Full record + related-memory neighbourhood
341
345
  quilltap memories tree <id|prefix> [--depth N] [--max-nodes N] # ASCII walk of the bidirectional related-memory graph
342
346
  quilltap memories status [--character <name|id>] [--port <n>] # Per-holder rollup, maintenance ledger + dangling-edge check
@@ -348,7 +352,7 @@ quilltap memories maintain --character Ariadne --phases consolidate # Queue a
348
352
  quilltap memories maintain --character all [--phases demote,retire] # Queue maintenance for every character
349
353
  ```
350
354
 
351
- Shared filter flags apply to `ls`, `find`, `grep`, and `status` where they make sense: `--character`, `--about` (with `self` / `none` shortcuts), `--source` (AUTO / MANUAL / CONSOLIDATED), `--tier` (hot / cold; both by default, cold rows are starred in the `src` column), `--chat` (with `none` for manual entries), `--project`, `--since`, `--until`, `--min-importance`, `--min-reinforced`, `--has-embedding` / `--no-embedding`. Sort flags (`--sort reinforced|importance|created|accessed|reinforcement-count|links`, plus `-r` to reverse) apply to `ls`, `find`, and `grep`. Names accept fuzzy substrings; ambiguous names print candidates and exit 2. `--json` is supported by every verb. `validate` is `status`'s terse twin — read-only; it counts dangling `relatedMemoryIds` edges, dangling `consolidatedFrom` members (digests naming notes that are gone), dangling `supersededById` pointers, and rows with no embedding. It exits 1 if any dangling link remains (a missing embedding only warns), and `--list` prints the offending IDs. `grep --semantic` defaults to `--top 20`, `--threshold 0.5`, and scopes to **one holder at a time**: `--character all` is rejected. The legacy `quilltap db memories --character <name>` verb remains, now with `--tier hot|cold` (rows with no tier count as hot).
355
+ Shared filter flags apply to `ls`, `find`, `grep`, and `status` where they make sense: `--character`, `--about` (with `self` / `none` shortcuts), `--source` (AUTO / MANUAL / CONSOLIDATED), `--tier` (hot / cold; both by default, cold rows are starred in the `src` column), `--chat` (with `none` for manual entries), `--project`, `--since`, `--until`, `--min-importance`, `--min-reinforced`, `--has-embedding` / `--no-embedding`. Sort flags (`--sort reinforced|importance|created|accessed|reinforcement-count|links`, plus `-r` / `--reverse` to flip it, `--limit N` (default 50) and `--full-titles` to stop chat titles being truncated to 32 characters) apply to `ls`, `find`, and `grep`. `grep -l` / `--paths-only` prints only the IDs of matching memories, one per line. Names accept fuzzy substrings; ambiguous names print candidates and exit 2. `--json` is supported by every verb. `validate` is `status`'s terse twin — read-only; it counts dangling `relatedMemoryIds` edges, dangling `consolidatedFrom` members (digests naming notes that are gone), dangling `supersededById` pointers, and rows with no embedding. It exits 1 if any dangling link remains (a missing embedding only warns), and `--list` prints the offending IDs. `grep --semantic` defaults to `--top 20`, `--threshold 0.5`, and scopes to **one holder at a time**: `--character all` is rejected. The legacy `quilltap db memories --character <name>` verb remains, now with `--tier hot|cold` (rows with no tier count as hot).
352
356
 
353
357
  Verbs that call the server (`grep --semantic`, `status`'s ledger, `maintain`, `validate --repair`) take `--port`; without it they use the port recorded in the `--instance` / `--data-dir` instance's lock, else 3000.
354
358
 
@@ -480,7 +484,13 @@ quilltap themes create sunset # Scaffold a new theme
480
484
  quilltap themes search "dark" # Search registries
481
485
  quilltap themes update # Check for theme updates
482
486
  quilltap themes registry list # List configured registries
483
- quilltap themes registry add <url> # Add a registry source
487
+ quilltap themes registry add <url> # Add a registry source (--key <pubkey>, --name <name>)
488
+ quilltap themes registry remove <name> # Remove a registry source
489
+ quilltap themes registry refresh # Refresh all registry indexes
490
+
491
+ # For registry operators
492
+ quilltap themes registry keygen [--output <dir>] # Generate an Ed25519 keypair
493
+ quilltap themes registry sign <dir> --key <private> # Sign a registry directory
484
494
  ```
485
495
 
486
496
  ## Shell Completion
@@ -559,7 +569,7 @@ Fish picks new completion files up automatically — no shell restart needed.
559
569
 
560
570
  Completions **parse the line rather than counting words**, so a flag typed anywhere the CLI itself accepts one does not derail them: `quilltap docs --instance Friday <TAB>` still offers the `docs` verbs. bash and zsh also reuse the `-i` / `-d` / `--passphrase` already on the line when looking store names up, so the names offered come from the instance you are addressing rather than the default one. fish completes `--mount` but not the positionals, and always reads the default instance.
561
571
 
562
- Dynamic completions shell out to `quilltap`'s own subcommands. If the active instance is encrypted and no passphrase is reachable, the completion silently returns nothing rather than prompting in the middle of a tab.
572
+ Dynamic completions shell out to `quilltap`'s own subcommands — `quilltap instances list --names-only` and `quilltap docs list --names-only`, which print one name per line and are handy in scripts too. If the active instance is encrypted and no passphrase is reachable, the completion silently returns nothing rather than prompting in the middle of a tab.
563
573
 
564
574
  ## Requirements
565
575
 
package/bin/quilltap.js CHANGED
@@ -103,7 +103,7 @@ Subcommands:
103
103
  file-verify Force-download cloud-evicted data files (iCloud, etc.)
104
104
  memory-diff <chatId> Dump existing memories and dry-run re-extraction for a chat
105
105
  recall-replay <chatId> Replay a turn's memory recall, old vs episodic ranking
106
- anchor-probe <characterId> Probe whether episodic anchor lines suppress Memory Gate reinforcement
106
+ anchor-probe <characterId> Probe whether episodic anchor lines suppress reinforcement at admission
107
107
  completion <shell> Generate a shell completion script (bash / zsh / fish)
108
108
 
109
109
  Options:
@@ -0,0 +1,54 @@
1
+ /**
2
+ * `quilltap migrations status` compares the ledger against the ids it reads
3
+ * out of `migrations/scripts/*.ts`. Most migrations declare their id through a
4
+ * `MIGRATION_ID` constant rather than a string literal; when the reader only
5
+ * understood literals, every one of those vanished from "source" and showed up
6
+ * as "retired" on any instance that had run it.
7
+ *
8
+ * @jest-environment node
9
+ */
10
+
11
+ 'use strict';
12
+
13
+ const fs = require('fs');
14
+ const path = require('path');
15
+
16
+ const { extractMigrationId } = require('../migrations-commands');
17
+
18
+ describe('extractMigrationId', () => {
19
+ it('reads a string-literal id', () => {
20
+ expect(extractMigrationId(`export const m = {\n id: 'foo-v1',\n};`)).toBe('foo-v1');
21
+ });
22
+
23
+ it('resolves an id declared through MIGRATION_ID', () => {
24
+ const src = `const MIGRATION_ID = 'bar-v1';\nexport const m = {\n id: MIGRATION_ID,\n};`;
25
+ expect(extractMigrationId(src)).toBe('bar-v1');
26
+ });
27
+
28
+ it('accepts a typed MIGRATION_ID constant', () => {
29
+ const src = `const MIGRATION_ID: string = 'baz-v1'\nexport const m = {\n id: MIGRATION_ID,\n};`;
30
+ expect(extractMigrationId(src)).toBe('baz-v1');
31
+ });
32
+
33
+ it('returns null when no id is declared', () => {
34
+ expect(extractMigrationId('export const nothing = 1;')).toBeNull();
35
+ });
36
+ });
37
+
38
+ describe('every migration script yields an id', () => {
39
+ const scriptsDir = path.join(__dirname, '..', '..', '..', '..', 'migrations', 'scripts');
40
+ const files = fs.existsSync(scriptsDir)
41
+ ? fs.readdirSync(scriptsDir).filter((f) => f.endsWith('.ts') && f !== 'index.ts')
42
+ : [];
43
+
44
+ it('finds the migration scripts', () => {
45
+ expect(files.length).toBeGreaterThan(100);
46
+ });
47
+
48
+ it('extracts an id from each one', () => {
49
+ const missing = files.filter(
50
+ (f) => !extractMigrationId(fs.readFileSync(path.join(scriptsDir, f), 'utf8'))
51
+ );
52
+ expect(missing).toEqual([]);
53
+ });
54
+ });
@@ -61,7 +61,7 @@ complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'instances' -d 'Registe
61
61
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'memories' -d 'Search and browse memories'
62
62
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'memory-diff' -d 'Memory extraction dry-run'
63
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 Memory Gate'
64
+ complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'anchor-probe' -d 'Probe anchor-line effect on admission'
65
65
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'logs' -d 'Tail or print log files'
66
66
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'migrations' -d 'Inspect migration status'
67
67
  complete -c quilltap -n '__quilltap_no_subcommand' -f -a 'maintenance' -d 'Run retention/cleanup sweeps'
@@ -43,7 +43,7 @@ _quilltap() {
43
43
  'memories:Search, browse, and graph memories'
44
44
  'memory-diff:Dump existing memories and dry-run re-extraction'
45
45
  'recall-replay:Replay a turn'\''s memory recall, old vs episodic ranking'
46
- 'anchor-probe:Probe whether episodic anchor lines suppress Memory Gate reinforcement'
46
+ 'anchor-probe:Probe whether episodic anchor lines suppress reinforcement at admission'
47
47
  'logs:Tail or print an instance log file'
48
48
  'migrations:Inspect migration status'
49
49
  'maintenance:Run retention/cleanup sweeps'
@@ -43,6 +43,19 @@ function parseFlags(args) {
43
43
 
44
44
  // ---------- migration list parsing ----------
45
45
 
46
+ /**
47
+ * A migration's id is either a string literal on the object (`id: 'foo-v1'`)
48
+ * or a module constant (`id: MIGRATION_ID` beside `const MIGRATION_ID = 'foo-v1'`).
49
+ * Missing the constant form drops those migrations from "source", which then
50
+ * report as retired on every instance that ran them.
51
+ */
52
+ function extractMigrationId(fileContent) {
53
+ const constMatch = fileContent.match(/^\s*const\s+MIGRATION_ID\s*(?::\s*\w+\s*)?=\s*['"]([^'"]+)['"]/m);
54
+ if (constMatch && /^\s*id:\s*MIGRATION_ID\b/m.test(fileContent)) return constMatch[1];
55
+ const literalMatch = fileContent.match(/^\s*id:\s*['"]([^'"]+)['"]/m);
56
+ return literalMatch ? literalMatch[1] : null;
57
+ }
58
+
46
59
  // Reads the registered migration list from migrations/scripts/index.ts and resolves each
47
60
  // entry's real `id:` value by reading the imported source file. Heuristic camelCase→kebab
48
61
  // conversion is unreliable (some migrations omit `-v1`, others use `-v2`), so we always
@@ -95,11 +108,11 @@ function extractMigrationsFromSource() {
95
108
  const filePath = path.join(scriptsDir, info.filePath + '.ts');
96
109
  if (!fs.existsSync(filePath)) continue;
97
110
  const fileContent = fs.readFileSync(filePath, 'utf-8');
98
- const idMatch = fileContent.match(/^\s*id:\s*['"]([^'"]+)['"]/m);
99
- if (!idMatch) continue;
111
+ const id = extractMigrationId(fileContent);
112
+ if (!id) continue;
100
113
  migrations.push({
101
- id: idMatch[1],
102
- description: info.comment || idMatch[1],
114
+ id,
115
+ description: info.comment || id,
103
116
  });
104
117
  }
105
118
 
@@ -321,4 +334,5 @@ async function migrationsCommand(args) {
321
334
 
322
335
  module.exports = {
323
336
  migrationsCommand,
337
+ extractMigrationId,
324
338
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quilltap",
3
- "version": "4.10.0-dev.175",
3
+ "version": "4.10.0-dev.183",
4
4
  "description": "Self-hosted AI workspace for writers, worldbuilders, and roleplayers. Run with npx quilltap.",
5
5
  "author": {
6
6
  "name": "Charles Sebold",