@teammates/recall 0.1.0 → 0.1.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 +12 -0
- package/dist/cli.js +19 -19
- package/dist/embeddings.js +4 -1
- package/dist/indexer.d.ts +3 -2
- package/dist/indexer.js +7 -6
- package/dist/search.js +3 -13
- package/package.json +34 -34
- package/src/cli.ts +275 -275
- package/src/embeddings.ts +46 -43
- package/src/index.ts +3 -3
- package/src/indexer.ts +204 -203
- package/src/search.ts +99 -107
- package/tsconfig.json +18 -18
package/README.md
CHANGED
|
@@ -93,6 +93,16 @@ teammates-recall status --dir ./.teammates
|
|
|
93
93
|
4. **Stores** the index at `.teammates/.index/<teammate>/` (gitignored)
|
|
94
94
|
5. **Searches** using Vectra's semantic similarity matching
|
|
95
95
|
|
|
96
|
+
## Auto-Sync
|
|
97
|
+
|
|
98
|
+
Every `search` call automatically detects new or changed memory files and indexes them before returning results. This is on by default — no manual `sync` or `index` step is needed.
|
|
99
|
+
|
|
100
|
+
**How it works:** The indexer compares file modification times against stored metadata. Only files that are new or changed since the last sync get re-indexed, so the overhead is minimal for most queries.
|
|
101
|
+
|
|
102
|
+
**Skip it when you need speed:** Pass `--no-sync` (CLI) or `skipSync: true` (library) to skip the check entirely. Useful for hot loops or large indexes where you control sync timing separately.
|
|
103
|
+
|
|
104
|
+
**Why this matters for agents:** Agents write memory files as plain markdown — they shouldn't need to know about index state or remember to run a sync command. Auto-sync closes the gap between "file written" and "file searchable" so agents can write-then-search in a single workflow without extra steps.
|
|
105
|
+
|
|
96
106
|
## Use From Any Agent
|
|
97
107
|
|
|
98
108
|
Any AI coding tool that can run shell commands can use recall:
|
|
@@ -134,6 +144,8 @@ const results = await search("database migration", {
|
|
|
134
144
|
teammatesDir: "./.teammates",
|
|
135
145
|
teammate: "atlas",
|
|
136
146
|
maxResults: 5,
|
|
147
|
+
maxChunks: 3, // max chunks per document (default: 3)
|
|
148
|
+
maxTokens: 500, // max tokens per section (default: 500)
|
|
137
149
|
});
|
|
138
150
|
|
|
139
151
|
// Search without auto-sync
|
package/dist/cli.js
CHANGED
|
@@ -3,24 +3,24 @@ import * as path from "node:path";
|
|
|
3
3
|
import * as fs from "node:fs/promises";
|
|
4
4
|
import { Indexer } from "./indexer.js";
|
|
5
5
|
import { search } from "./search.js";
|
|
6
|
-
const HELP = `
|
|
7
|
-
teammates-recall — Semantic memory search for teammates
|
|
8
|
-
|
|
9
|
-
Usage:
|
|
10
|
-
teammates-recall index [options] Full rebuild of all indexes
|
|
11
|
-
teammates-recall sync [options] Sync new/changed files into indexes
|
|
12
|
-
teammates-recall add <file> [options] Add a single file to a teammate's index
|
|
13
|
-
teammates-recall search <query> [options] Search teammate memories (auto-syncs)
|
|
14
|
-
teammates-recall status [options] Show index status
|
|
15
|
-
|
|
16
|
-
Options:
|
|
17
|
-
--dir <path> Path to .teammates directory (default: ./.teammates)
|
|
18
|
-
--teammate <name> Limit to a specific teammate
|
|
19
|
-
--results <n> Max results (default: 5)
|
|
20
|
-
--model <name> Embedding model (default: Xenova/all-MiniLM-L6-v2)
|
|
21
|
-
--no-sync Skip auto-sync before search
|
|
22
|
-
--json Output as JSON
|
|
23
|
-
--help Show this help
|
|
6
|
+
const HELP = `
|
|
7
|
+
teammates-recall — Semantic memory search for teammates
|
|
8
|
+
|
|
9
|
+
Usage:
|
|
10
|
+
teammates-recall index [options] Full rebuild of all indexes
|
|
11
|
+
teammates-recall sync [options] Sync new/changed files into indexes
|
|
12
|
+
teammates-recall add <file> [options] Add a single file to a teammate's index
|
|
13
|
+
teammates-recall search <query> [options] Search teammate memories (auto-syncs)
|
|
14
|
+
teammates-recall status [options] Show index status
|
|
15
|
+
|
|
16
|
+
Options:
|
|
17
|
+
--dir <path> Path to .teammates directory (default: ./.teammates)
|
|
18
|
+
--teammate <name> Limit to a specific teammate
|
|
19
|
+
--results <n> Max results (default: 5)
|
|
20
|
+
--model <name> Embedding model (default: Xenova/all-MiniLM-L6-v2)
|
|
21
|
+
--no-sync Skip auto-sync before search
|
|
22
|
+
--json Output as JSON
|
|
23
|
+
--help Show this help
|
|
24
24
|
`.trim();
|
|
25
25
|
function parseArgs(argv) {
|
|
26
26
|
const args = {
|
|
@@ -199,7 +199,7 @@ async function cmdStatus(args) {
|
|
|
199
199
|
const status = {};
|
|
200
200
|
for (const teammate of teammates) {
|
|
201
201
|
const { files } = await indexer.collectFiles(teammate);
|
|
202
|
-
const indexPath =
|
|
202
|
+
const indexPath = indexer.indexPath(teammate);
|
|
203
203
|
let indexed = false;
|
|
204
204
|
try {
|
|
205
205
|
await fs.access(indexPath);
|
package/dist/embeddings.js
CHANGED
|
@@ -13,7 +13,10 @@ export class LocalEmbeddings {
|
|
|
13
13
|
async createEmbeddings(inputs) {
|
|
14
14
|
try {
|
|
15
15
|
const extractor = await this._getExtractor();
|
|
16
|
-
const texts = Array.isArray(inputs) ? inputs : [inputs];
|
|
16
|
+
const texts = (Array.isArray(inputs) ? inputs : [inputs]).filter((t) => t.trim().length > 0);
|
|
17
|
+
if (texts.length === 0) {
|
|
18
|
+
return { status: "success", output: [] };
|
|
19
|
+
}
|
|
17
20
|
const output = await extractor(texts, {
|
|
18
21
|
pooling: "mean",
|
|
19
22
|
normalize: true,
|
package/dist/indexer.d.ts
CHANGED
|
@@ -13,13 +13,14 @@ interface TeammateFiles {
|
|
|
13
13
|
}
|
|
14
14
|
/**
|
|
15
15
|
* Indexes teammate memory files (MEMORIES.md + memory/*.md) into Vectra.
|
|
16
|
-
* One index per teammate, stored at .teammates
|
|
16
|
+
* One index per teammate, stored at .teammates/<name>/.index/
|
|
17
17
|
*/
|
|
18
18
|
export declare class Indexer {
|
|
19
19
|
private _config;
|
|
20
20
|
private _embeddings;
|
|
21
21
|
constructor(config: IndexerConfig);
|
|
22
|
-
|
|
22
|
+
/** Get the index path for a specific teammate */
|
|
23
|
+
indexPath(teammate: string): string;
|
|
23
24
|
/**
|
|
24
25
|
* Discover all teammate directories (folders containing SOUL.md).
|
|
25
26
|
*/
|
package/dist/indexer.js
CHANGED
|
@@ -4,7 +4,7 @@ import * as fs from "node:fs/promises";
|
|
|
4
4
|
import * as path from "node:path";
|
|
5
5
|
/**
|
|
6
6
|
* Indexes teammate memory files (MEMORIES.md + memory/*.md) into Vectra.
|
|
7
|
-
* One index per teammate, stored at .teammates
|
|
7
|
+
* One index per teammate, stored at .teammates/<name>/.index/
|
|
8
8
|
*/
|
|
9
9
|
export class Indexer {
|
|
10
10
|
_config;
|
|
@@ -13,8 +13,9 @@ export class Indexer {
|
|
|
13
13
|
this._config = config;
|
|
14
14
|
this._embeddings = new LocalEmbeddings(config.model);
|
|
15
15
|
}
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
/** Get the index path for a specific teammate */
|
|
17
|
+
indexPath(teammate) {
|
|
18
|
+
return path.join(this._config.teammatesDir, teammate, ".index");
|
|
18
19
|
}
|
|
19
20
|
/**
|
|
20
21
|
* Discover all teammate directories (folders containing SOUL.md).
|
|
@@ -78,7 +79,7 @@ export class Indexer {
|
|
|
78
79
|
const { files } = await this.collectFiles(teammate);
|
|
79
80
|
if (files.length === 0)
|
|
80
81
|
return 0;
|
|
81
|
-
const indexPath =
|
|
82
|
+
const indexPath = this.indexPath(teammate);
|
|
82
83
|
const index = new LocalDocumentIndex({
|
|
83
84
|
folderPath: indexPath,
|
|
84
85
|
embeddings: this._embeddings,
|
|
@@ -119,7 +120,7 @@ export class Indexer {
|
|
|
119
120
|
const text = await fs.readFile(absolutePath, "utf-8");
|
|
120
121
|
if (text.trim().length === 0)
|
|
121
122
|
return;
|
|
122
|
-
const indexPath =
|
|
123
|
+
const indexPath = this.indexPath(teammate);
|
|
123
124
|
const index = new LocalDocumentIndex({
|
|
124
125
|
folderPath: indexPath,
|
|
125
126
|
embeddings: this._embeddings,
|
|
@@ -137,7 +138,7 @@ export class Indexer {
|
|
|
137
138
|
const { files } = await this.collectFiles(teammate);
|
|
138
139
|
if (files.length === 0)
|
|
139
140
|
return 0;
|
|
140
|
-
const indexPath =
|
|
141
|
+
const indexPath = this.indexPath(teammate);
|
|
141
142
|
const index = new LocalDocumentIndex({
|
|
142
143
|
folderPath: indexPath,
|
|
143
144
|
embeddings: this._embeddings,
|
package/dist/search.js
CHANGED
|
@@ -1,20 +1,18 @@
|
|
|
1
1
|
import { LocalDocumentIndex } from "vectra";
|
|
2
2
|
import { LocalEmbeddings } from "./embeddings.js";
|
|
3
3
|
import { Indexer } from "./indexer.js";
|
|
4
|
-
import * as path from "node:path";
|
|
5
4
|
import * as fs from "node:fs/promises";
|
|
6
5
|
/**
|
|
7
6
|
* Search teammate memories using semantic + keyword search.
|
|
8
7
|
*/
|
|
9
8
|
export async function search(query, options) {
|
|
10
|
-
const indexRoot = path.join(options.teammatesDir, ".index");
|
|
11
9
|
const embeddings = new LocalEmbeddings(options.model);
|
|
10
|
+
const indexer = new Indexer({ teammatesDir: options.teammatesDir, model: options.model });
|
|
12
11
|
const maxResults = options.maxResults ?? 5;
|
|
13
12
|
const maxChunks = options.maxChunks ?? 3;
|
|
14
13
|
const maxTokens = options.maxTokens ?? 500;
|
|
15
14
|
// Auto-sync: upsert any new/changed files before searching
|
|
16
15
|
if (!options.skipSync) {
|
|
17
|
-
const indexer = new Indexer({ teammatesDir: options.teammatesDir, model: options.model });
|
|
18
16
|
if (options.teammate) {
|
|
19
17
|
await indexer.syncTeammate(options.teammate);
|
|
20
18
|
}
|
|
@@ -28,19 +26,11 @@ export async function search(query, options) {
|
|
|
28
26
|
teammates = [options.teammate];
|
|
29
27
|
}
|
|
30
28
|
else {
|
|
31
|
-
|
|
32
|
-
const entries = await fs.readdir(indexRoot, { withFileTypes: true });
|
|
33
|
-
teammates = entries
|
|
34
|
-
.filter((e) => e.isDirectory())
|
|
35
|
-
.map((e) => e.name);
|
|
36
|
-
}
|
|
37
|
-
catch {
|
|
38
|
-
return [];
|
|
39
|
-
}
|
|
29
|
+
teammates = await indexer.discoverTeammates();
|
|
40
30
|
}
|
|
41
31
|
const allResults = [];
|
|
42
32
|
for (const teammate of teammates) {
|
|
43
|
-
const indexPath =
|
|
33
|
+
const indexPath = indexer.indexPath(teammate);
|
|
44
34
|
try {
|
|
45
35
|
await fs.access(indexPath);
|
|
46
36
|
}
|
package/package.json
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@teammates/recall",
|
|
3
|
-
"version": "0.1.
|
|
4
|
-
"description": "Local semantic memory search for teammates. Indexes MEMORIES.md and daily logs using Vectra + transformers.js.",
|
|
5
|
-
"type": "module",
|
|
6
|
-
"main": "dist/index.js",
|
|
7
|
-
"types": "dist/index.d.ts",
|
|
8
|
-
"bin": {
|
|
9
|
-
"teammates-recall": "dist/cli.js"
|
|
10
|
-
},
|
|
11
|
-
"scripts": {
|
|
12
|
-
"build": "tsc",
|
|
13
|
-
"dev": "tsc --watch"
|
|
14
|
-
},
|
|
15
|
-
"keywords": [
|
|
16
|
-
"teammates",
|
|
17
|
-
"ai",
|
|
18
|
-
"memory",
|
|
19
|
-
"vector-search",
|
|
20
|
-
"embeddings"
|
|
21
|
-
],
|
|
22
|
-
"license": "MIT",
|
|
23
|
-
"dependencies": {
|
|
24
|
-
"@huggingface/transformers": "^3.0.0",
|
|
25
|
-
"vectra": "^0.9.0"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@types/node": "^20.0.0",
|
|
29
|
-
"typescript": "^5.5.0"
|
|
30
|
-
},
|
|
31
|
-
"engines": {
|
|
32
|
-
"node": ">=20.0.0"
|
|
33
|
-
}
|
|
34
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@teammates/recall",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Local semantic memory search for teammates. Indexes MEMORIES.md and daily logs using Vectra + transformers.js.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"teammates-recall": "dist/cli.js"
|
|
10
|
+
},
|
|
11
|
+
"scripts": {
|
|
12
|
+
"build": "tsc",
|
|
13
|
+
"dev": "tsc --watch"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"teammates",
|
|
17
|
+
"ai",
|
|
18
|
+
"memory",
|
|
19
|
+
"vector-search",
|
|
20
|
+
"embeddings"
|
|
21
|
+
],
|
|
22
|
+
"license": "MIT",
|
|
23
|
+
"dependencies": {
|
|
24
|
+
"@huggingface/transformers": "^3.0.0",
|
|
25
|
+
"vectra": "^0.9.0"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"@types/node": "^20.0.0",
|
|
29
|
+
"typescript": "^5.5.0"
|
|
30
|
+
},
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20.0.0"
|
|
33
|
+
}
|
|
34
|
+
}
|