claude-recall 0.36.0 → 0.36.1

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
@@ -211,6 +211,7 @@ claude-recall search "preference"
211
211
  claude-recall status # Installation health: hooks, MCP, DB path, project ID
212
212
  claude-recall stats # What's in the DB for this project (--global for all)
213
213
 
214
+ claude-recall list # List all memories, newest first (-t <type>, --all, --json, --global)
214
215
  claude-recall search "query" # Search this project's memories (--global, --json, --project <id>)
215
216
  claude-recall failures # What broke and what fixed it
216
217
  claude-recall outcomes # Outcome-aware learning status
@@ -273,6 +274,7 @@ claude-recall hooks test-enforcement # Test if search enforcer hook works
273
274
 
274
275
  # ── Memory ───────────────────────────────────────────────────────────
275
276
  claude-recall stats # Memory statistics (--global for all projects)
277
+ claude-recall list # List memories, newest first (-t <type>, --all, --json, --global)
276
278
  claude-recall search "query" # Search memories (--global, --json, --project <id>)
277
279
  claude-recall store "content" # Store memory directly
278
280
  claude-recall store "content" -t <type> # Type: preference, correction, failure, devops, project-knowledge
@@ -733,6 +733,85 @@ class ClaudeRecallCLI {
733
733
  });
734
734
  this.logger.info('CLI', 'Search completed', { query, resultCount: results.length });
735
735
  }
736
+ /**
737
+ * List (enumerate) stored memories — no query, no ranking, no relevance cap.
738
+ *
739
+ * Fills the gap between `search` (needs a query, returns ranked top-N) and
740
+ * `stats` (counts only, no content). Backed by getAllByProject/getAllMemories
741
+ * so it returns EVERYTHING in scope, newest first. Shows the numeric id and
742
+ * key so rows can be fed to `delete <key>` / `rules promote <id>`.
743
+ */
744
+ listMemories(options) {
745
+ // Scope resolution mirrors search(): current project (+ universal/unscoped)
746
+ // by default, a named project, or every project with --global.
747
+ let memories;
748
+ let scopeLabel;
749
+ if (options.global) {
750
+ memories = this.memoryService.getAllMemories();
751
+ scopeLabel = 'all projects';
752
+ }
753
+ else {
754
+ const projectId = options.project || config_1.ConfigService.getInstance().getProjectId();
755
+ memories = this.memoryService.getAllByProject(projectId);
756
+ scopeLabel = `project: ${projectId}`;
757
+ }
758
+ if (options.type) {
759
+ memories = memories.filter(m => m.type === options.type);
760
+ }
761
+ // Newest first — most recently learned memory at the top.
762
+ memories.sort((a, b) => (b.timestamp || 0) - (a.timestamp || 0));
763
+ const total = memories.length;
764
+ const limit = options.all ? total : (0, parse_utils_1.parsePositiveInt)(options.limit, 'limit', 20);
765
+ const shown = memories.slice(0, limit);
766
+ if (options.json) {
767
+ console.log(JSON.stringify(shown.map(m => ({
768
+ id: m.id,
769
+ key: m.key,
770
+ type: m.type,
771
+ value: m.value,
772
+ project_id: m.project_id,
773
+ scope: m.scope,
774
+ is_active: m.is_active,
775
+ timestamp: m.timestamp,
776
+ })), null, 2));
777
+ return;
778
+ }
779
+ const typeFilter = options.type ? ` of type "${options.type}"` : '';
780
+ console.log(`\n🧠 Memories (${scopeLabel})\n`);
781
+ console.log(`${total} memor${total === 1 ? 'y' : 'ies'}${typeFilter}${total > shown.length ? ` — showing ${shown.length} (use --all or --limit)` : ''}\n`);
782
+ if (shown.length === 0) {
783
+ console.log('No memories found.\n');
784
+ return;
785
+ }
786
+ for (const m of shown) {
787
+ const inactive = m.is_active === false ? ' 💤 inactive' : '';
788
+ const when = m.timestamp ? new Date(m.timestamp).toLocaleString() : 'unknown time';
789
+ console.log(`[${m.id ?? '?'}] ${m.type}${inactive} · ${when}`);
790
+ console.log(` ${this.truncateContent(this.extractContent(m.value))}`);
791
+ console.log(` key: ${m.key}`);
792
+ console.log('');
793
+ }
794
+ this.logger.info('CLI', 'List completed', { scope: scopeLabel, total, shown: shown.length });
795
+ }
796
+ /**
797
+ * Pull the human-readable text out of a memory value, whether it's a raw
798
+ * string, a JSON string, or a structured object with a content/value field.
799
+ */
800
+ extractContent(value) {
801
+ let v = value;
802
+ if (typeof v === 'string') {
803
+ try {
804
+ v = JSON.parse(v);
805
+ }
806
+ catch {
807
+ return value;
808
+ }
809
+ }
810
+ if (v && typeof v === 'object') {
811
+ return String(v.content ?? v.value ?? v.text ?? JSON.stringify(v));
812
+ }
813
+ return String(v);
814
+ }
736
815
  /**
737
816
  * Export memories to a file
738
817
  */
@@ -1869,6 +1948,28 @@ async function main() {
1869
1948
  });
1870
1949
  process.exit(0);
1871
1950
  });
1951
+ // List command — enumerate memories (no query, unlike `search`)
1952
+ program
1953
+ .command('list')
1954
+ .description('List stored memories (no query needed; newest first)')
1955
+ .option('-t, --type <type>', 'Filter by memory type (preference, failure, devops, ...)')
1956
+ .option('-l, --limit <number>', 'Maximum memories to show', '20')
1957
+ .option('--all', 'Show every memory in scope (ignores --limit)')
1958
+ .option('--project <id>', 'List a specific project (includes universal memories)')
1959
+ .option('--global', 'List memories across all projects')
1960
+ .option('--json', 'Output as JSON')
1961
+ .action((options) => {
1962
+ const cli = new ClaudeRecallCLI(program.opts());
1963
+ cli.listMemories({
1964
+ type: options.type,
1965
+ limit: (0, parse_utils_1.parsePositiveInt)(options.limit, 'limit', 20),
1966
+ all: options.all,
1967
+ project: options.project,
1968
+ global: options.global,
1969
+ json: options.json,
1970
+ });
1971
+ process.exit(0);
1972
+ });
1872
1973
  // Stats command
1873
1974
  program
1874
1975
  .command('stats')
@@ -0,0 +1,69 @@
1
+ # Wiring a project to the Claude Code subscription (no API key)
2
+
3
+ The mechanism: don't call the Anthropic API at all — **shell out to the `claude`
4
+ CLI**, which is already authenticated by the user's Claude Code subscription
5
+ login. No key, no billing setup; it borrows the session the user logged into.
6
+
7
+ > Applies to any project. Replace the placeholder name (`MY_APP_NESTED`) with
8
+ > your own convention.
9
+
10
+ ## The core idea
11
+
12
+ `claude -p "<prompt>"` runs a one-shot, non-interactive completion using the
13
+ subscription auth. Your project spawns it as a subprocess and reads stdout.
14
+ That's the whole trick.
15
+
16
+ ```bash
17
+ claude -p "Summarize this in one line: ..." # prints completion to stdout, exits
18
+ ```
19
+
20
+ ## Wiring it into any project
21
+
22
+ Spawn it non-interactively (never through a shell string — pass args to avoid
23
+ injection):
24
+
25
+ ```ts
26
+ import { spawn } from 'node:child_process';
27
+
28
+ function complete(prompt: string, timeoutMs = 60_000): Promise<string> {
29
+ return new Promise((resolve, reject) => {
30
+ const p = spawn('claude', ['-p', prompt], {
31
+ stdio: ['ignore', 'pipe', 'pipe'],
32
+ timeout: timeoutMs,
33
+ env: { ...process.env, MY_APP_NESTED: '1' }, // recursion guard
34
+ });
35
+ let out = '', err = '';
36
+ p.stdout.on('data', d => (out += d));
37
+ p.stderr.on('data', d => (err += d));
38
+ p.on('close', code =>
39
+ code === 0 ? resolve(out.trim()) : reject(new Error(err || `exit ${code}`)));
40
+ p.on('error', reject);
41
+ });
42
+ }
43
+ ```
44
+
45
+ ## The things that bite people
46
+
47
+ - **Recursion.** If your code runs *inside* a Claude Code session (a hook, an
48
+ MCP tool) and then spawns `claude -p`, that child can trigger the same hook
49
+ and fork-bomb. Set a sentinel env var on the child and bail at the top of your
50
+ entry point if it's present.
51
+ - **Availability.** Require `claude` to be installed and logged in. If it's not
52
+ on `PATH` (`ENOENT`), surface a clear error telling the user to install the
53
+ CLI and run `claude login` — don't crash silently.
54
+ - **Timeouts.** The CLI is a full agent process, slower and heavier than a raw
55
+ API call. Always set a timeout and handle the null result; never block your
56
+ main flow on it.
57
+ - **Structured output.** `-p` returns prose. If you need JSON, say so in the
58
+ prompt and parse defensively (treat malformed output as a no-op). Add
59
+ `--output-format json` if you want the CLI's own envelope instead.
60
+
61
+ ## Setup, once
62
+
63
+ ```bash
64
+ claude login # authenticate the subscription — no key involved
65
+ claude -p "ping" # verify it responds
66
+ ```
67
+
68
+ That's all a fresh clone needs: an installed, logged-in `claude` CLI. No
69
+ secrets, no `ANTHROPIC_API_KEY`, no billing configuration.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-recall",
3
- "version": "0.36.0",
3
+ "version": "0.36.1",
4
4
  "description": "Persistent memory for Claude Code and Pi with native Skills integration, automatic capture, failure learning, and project scoping",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -102,7 +102,7 @@
102
102
  "typescript-eslint": "^8.63.0"
103
103
  },
104
104
  "dependencies": {
105
- "@anthropic-ai/sdk": "^0.110.0",
105
+ "@anthropic-ai/sdk": "^0.111.0",
106
106
  "better-sqlite3": "^12.2.0",
107
107
  "chalk": "^5.5.0",
108
108
  "commander": "^14.0.3"