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.
|
|
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.
|
|
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"
|