@todoforai/tfa-memory 0.4.5 → 0.5.0
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 +11 -0
- package/dist/cli.js +58 -187
- package/package.json +2 -2
package/README.md
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# @todoforai/tfa-memory
|
|
2
|
+
|
|
3
|
+
Search past TODOforAI conversations from the shell. Read-only; reuses your bridge credentials.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
tfa-memory search "how do we deploy" --mode fused # lexical (default) | semantic | fused
|
|
7
|
+
tfa-memory get todo/<todoId>:<messageId> # one whole message
|
|
8
|
+
tfa-memory around todo/<todoId>:<messageId> --before 5 --after 20
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Memory itself is files, not this tool: `projects/<id>/memory/` (team) and `projects/<id>/me/memory/` (personal) are pasted into every agent prompt; `context/` beside them is read on demand.
|
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,6 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
4
4
|
|
|
5
5
|
// src/cli.ts
|
|
6
6
|
import { parseArgs } from "node:util";
|
|
7
|
-
import { readFileSync as readFileSync4 } from "node:fs";
|
|
8
7
|
|
|
9
8
|
// ../tfa-subagent/dist/auth.js
|
|
10
9
|
import { readFileSync, writeFileSync, mkdirSync, chmodSync } from "node:fs";
|
|
@@ -512,8 +511,8 @@ function checkForUpdates(pkg) {
|
|
|
512
511
|
// package.json
|
|
513
512
|
var package_default = {
|
|
514
513
|
name: "@todoforai/tfa-memory",
|
|
515
|
-
version: "0.
|
|
516
|
-
description: "Zero-config TODOforAI
|
|
514
|
+
version: "0.5.0",
|
|
515
|
+
description: "Zero-config TODOforAI CLI to search past TODO conversations (full-text and semantic), print one message or read the conversation around it. Read-only; memory itself lives in project files. Reuses bridge credentials.",
|
|
517
516
|
type: "module",
|
|
518
517
|
bin: {
|
|
519
518
|
"tfa-memory": "dist/cli.js"
|
|
@@ -542,71 +541,44 @@ var package_default = {
|
|
|
542
541
|
|
|
543
542
|
// src/cli.ts
|
|
544
543
|
var isEntrypoint = __require.main == __require.module === true || !!process.argv[1] && import.meta.url === `file://${process.argv[1]}`;
|
|
544
|
+
var MEMORY_FILES = `Memory is files now: save durable facts as short files in todoforai:projects/<projectId>/me/memory/ (personal) or …/memory/ (team) — they are pasted into every agent prompt; longer material goes in context/. Notes saved earlier with \`tfa-memory add\` are in todoforai:projects/<projectId>/me/context/notes/.`;
|
|
545
545
|
function printHelp() {
|
|
546
|
-
console.log(`tfa-memory —
|
|
546
|
+
console.log(`tfa-memory — search past TODO conversations (read-only).
|
|
547
547
|
|
|
548
|
-
Every
|
|
549
|
-
|
|
550
|
-
|
|
548
|
+
Every message of every TODO you can see is addressed \`todo/<todoId>:<messageId>\`.
|
|
549
|
+
Private TODOs are searchable only in personal contexts: in agent shells
|
|
550
|
+
TODOFORAI_TODO_ID names the conversation asking, and a shared one cannot read them.
|
|
551
551
|
|
|
552
|
-
|
|
553
|
-
every TODO under the read-only \`todo\` source (\`todo/<todoId>:<messageId>\`,
|
|
554
|
-
live from the backend index, nothing separate to sync). \`--source todo\` searches
|
|
555
|
-
past TODO messages only; \`get todo/<todoId>:<messageId>\` prints the whole message.
|
|
556
|
-
|
|
557
|
-
Owner-private TODOs are searchable in personal contexts. In agent shells,
|
|
558
|
-
TODOFORAI_TODO_ID supplies the destination; shared conversations cannot read
|
|
559
|
-
private TODOs or account-private saved notes. --todo selects a source, not an audience.
|
|
552
|
+
${MEMORY_FILES}
|
|
560
553
|
|
|
561
554
|
Usage:
|
|
562
555
|
tfa-memory <command> [options] [args...]
|
|
563
556
|
|
|
564
557
|
Commands:
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
live Re-render L2 working memory (the always-on prompt block)
|
|
569
|
-
ls List saved rows, newest first (todo messages excluded)
|
|
570
|
-
get <source/ref> Print full content of one row
|
|
571
|
-
around <source/ref> Neighbours of one row: todo messages in chat order, saved rows by time within the source
|
|
572
|
-
rm <source/ref> Delete one row
|
|
573
|
-
rm --source <name> Delete every row in a source
|
|
574
|
-
sources List sources with counts and last write
|
|
558
|
+
search <query> Search past TODO messages
|
|
559
|
+
get <address> Print one whole message
|
|
560
|
+
around <address> Messages around one, in chat order
|
|
575
561
|
login Device login — mint & persist credentials
|
|
576
562
|
whoami Print resolved auth context
|
|
577
563
|
|
|
578
564
|
Options:
|
|
579
|
-
-s, --source <a,b> Restrict search/ls to sources (comma-separated; "todo" = TODO messages)
|
|
580
|
-
--todo <id> Restrict a todo-message search to one TODO
|
|
581
|
-
-B, --budget <n> Token budget for \`live\` (default 1500, 100–20000)
|
|
582
565
|
--mode lexical|semantic|fused lexical: all words, newest first (default) · semantic: meaning · fused: best recall
|
|
583
|
-
--
|
|
584
|
-
--
|
|
585
|
-
--
|
|
586
|
-
--
|
|
587
|
-
-n, --limit <n> Max
|
|
566
|
+
--todo <id> Search one TODO only
|
|
567
|
+
--exclude <address> Leave out an already-read message (repeatable), to page past it
|
|
568
|
+
--before <n> around: messages before the pivot (default 2, max 200)
|
|
569
|
+
--after <n> around: messages after the pivot (default 2, max 200); large = read the rest
|
|
570
|
+
-n, --limit <n> Max results (default 5, max 50)
|
|
588
571
|
-j, --json Machine-readable output
|
|
589
572
|
--api-token <token> Override API token
|
|
590
|
-
--api-url <url> Override backend URL
|
|
591
|
-
--memory-url <url> Override memory-manager URL (default: derived — api.<d> → memory.<d>; localhost/IP → :8900)
|
|
573
|
+
--api-url <url> Override backend URL
|
|
592
574
|
-h, --help Show this help
|
|
593
575
|
-v, --version Show version
|
|
594
576
|
|
|
595
577
|
Examples:
|
|
596
|
-
tfa-memory add "postgres prod runs on hetzner-3, failover is manual"
|
|
597
|
-
tfa-memory add deploy-notes/rollback "run scripts/rollback.sh, then verify /health"
|
|
598
|
-
cat docs/auth.md | tfa-memory add acme/docs/auth.md -
|
|
599
578
|
tfa-memory search "rollback procedure"
|
|
600
|
-
tfa-memory search "how do we deploy" --mode
|
|
601
|
-
tfa-memory search "android build" --source todo
|
|
602
|
-
tfa-memory live
|
|
603
|
-
tfa-memory live -B 800
|
|
579
|
+
tfa-memory search "how do we deploy" --mode fused
|
|
604
580
|
tfa-memory get todo/<todoId>:<messageId>
|
|
605
|
-
tfa-memory
|
|
606
|
-
tfa-memory get deploy-notes/rollback
|
|
607
|
-
tfa-memory rm deploy-notes/rollback
|
|
608
|
-
tfa-memory rm --source acme
|
|
609
|
-
tfa-memory sources
|
|
581
|
+
tfa-memory around todo/<todoId>:<messageId> --before 5 --after 20
|
|
610
582
|
`);
|
|
611
583
|
}
|
|
612
584
|
function die(msg, code = 1) {
|
|
@@ -614,35 +586,15 @@ function die(msg, code = 1) {
|
|
|
614
586
|
process.exit(code);
|
|
615
587
|
}
|
|
616
588
|
function splitAddr(addr) {
|
|
617
|
-
const
|
|
618
|
-
if (
|
|
619
|
-
die(`invalid address "${addr}" — expected
|
|
620
|
-
return [
|
|
621
|
-
}
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
} else if (u.hostname.startsWith("api.")) {
|
|
627
|
-
u.hostname = "memory." + u.hostname.slice(4);
|
|
628
|
-
} else {
|
|
629
|
-
u.hostname = "memory." + u.hostname;
|
|
630
|
-
}
|
|
631
|
-
return u.origin;
|
|
632
|
-
}
|
|
633
|
-
function resolveMemoryUrl(flag, apiUrl) {
|
|
634
|
-
return flag || process.env.MEMORY_URL || deriveMemoryUrl(apiUrl);
|
|
635
|
-
}
|
|
636
|
-
async function api(ctx, method, path2, body) {
|
|
637
|
-
const init = {
|
|
638
|
-
method,
|
|
639
|
-
headers: {
|
|
640
|
-
...body !== undefined && { "content-type": "application/json" },
|
|
641
|
-
...process.env.TODOFORAI_TODO_ID && { "x-tfa-todo-id": process.env.TODOFORAI_TODO_ID }
|
|
642
|
-
},
|
|
643
|
-
body: body !== undefined ? JSON.stringify(body) : undefined
|
|
644
|
-
};
|
|
645
|
-
const { res, token } = await fetchWithRenew(`${ctx.memoryUrl}/v1${path2}`, init, { apiUrl: ctx.apiUrl, token: ctx.token, renewOn: [401, 403] });
|
|
589
|
+
const m = /^todo\/([^:/]+):([^:/]+)$/.exec(addr);
|
|
590
|
+
if (!m)
|
|
591
|
+
die(`invalid address "${addr}" — expected todo/<todoId>:<messageId>`, 2);
|
|
592
|
+
return { todoId: m[1], messageId: m[2] };
|
|
593
|
+
}
|
|
594
|
+
var mount = (token) => token.startsWith("dst_") ? "/dst/v1" : "/api/v1";
|
|
595
|
+
async function api(ctx, path2) {
|
|
596
|
+
const headers = process.env.TODOFORAI_TODO_ID ? { "x-tfa-todo-id": process.env.TODOFORAI_TODO_ID } : undefined;
|
|
597
|
+
const { res, token } = await fetchWithRenew(`${ctx.apiUrl}${mount(ctx.token)}${path2}`, { headers }, { apiUrl: ctx.apiUrl, token: ctx.token });
|
|
646
598
|
ctx.token = token;
|
|
647
599
|
if (!res.ok) {
|
|
648
600
|
const text = await res.text();
|
|
@@ -650,28 +602,33 @@ async function api(ctx, method, path2, body) {
|
|
|
650
602
|
try {
|
|
651
603
|
msg = JSON.parse(text)?.message ?? text;
|
|
652
604
|
} catch {}
|
|
653
|
-
die(`HTTP ${res.status}: ${msg}`);
|
|
605
|
+
die(res.status === 403 || res.status === 404 ? `not found or not yours (${res.status})` : `HTTP ${res.status}: ${msg}`);
|
|
654
606
|
}
|
|
655
|
-
return
|
|
607
|
+
return await res.json();
|
|
656
608
|
}
|
|
609
|
+
var messageText = (m) => [m.content, ...(m.blocks ?? []).flatMap((b) => [b.content, b.cmd, b.path])].filter((p) => !!p?.trim()).join(`
|
|
610
|
+
`);
|
|
611
|
+
var rowOf = (todoId, m) => ({ address: `todo/${todoId}:${m.id}`, createdAt: m.createdAt, content: messageText(m), meta: { projectId: m.projectId, role: m.role } });
|
|
657
612
|
function printRows(rows, json, snippet = 200) {
|
|
658
613
|
if (json)
|
|
659
|
-
return console.log(JSON.stringify(rows
|
|
614
|
+
return console.log(JSON.stringify(rows, null, process.stdout.isTTY ? 2 : 0));
|
|
660
615
|
if (!rows.length)
|
|
661
616
|
return console.log("(no results)");
|
|
662
|
-
for (const r of rows)
|
|
663
|
-
|
|
664
|
-
console.log(`${r.address} ${new Date(r.createdAt).toISOString().slice(0, 16)} ${oneline}`);
|
|
665
|
-
}
|
|
617
|
+
for (const r of rows)
|
|
618
|
+
console.log(`${r.address} ${new Date(r.createdAt).toISOString().slice(0, 16)} ${r.content.replace(/\s+/g, " ").slice(0, snippet)}`);
|
|
666
619
|
}
|
|
667
620
|
var qs = (params) => {
|
|
668
621
|
const p = new URLSearchParams;
|
|
669
622
|
for (const [k, v] of Object.entries(params))
|
|
670
623
|
if (v !== undefined)
|
|
671
624
|
p.set(k, String(v));
|
|
672
|
-
|
|
673
|
-
return s ? `?${s}` : "";
|
|
625
|
+
return p.size ? `?${p}` : "";
|
|
674
626
|
};
|
|
627
|
+
async function around(ctx, addr, before, after) {
|
|
628
|
+
const { todoId, messageId } = splitAddr(addr);
|
|
629
|
+
const res = await api(ctx, `/todos/${encodeURIComponent(todoId)}/messages/around${qs({ messageId, before, after })}`);
|
|
630
|
+
return { ...res, rows: res.messages.map((m) => rowOf(todoId, m)) };
|
|
631
|
+
}
|
|
675
632
|
async function main() {
|
|
676
633
|
await handleLogin(process.argv.slice(2), "tfa-memory");
|
|
677
634
|
const { values, positionals } = parseArgs({
|
|
@@ -679,8 +636,6 @@ async function main() {
|
|
|
679
636
|
options: {
|
|
680
637
|
source: { type: "string", short: "s" },
|
|
681
638
|
todo: { type: "string" },
|
|
682
|
-
budget: { type: "string", short: "B" },
|
|
683
|
-
meta: { type: "string" },
|
|
684
639
|
mode: { type: "string" },
|
|
685
640
|
exclude: { type: "string", multiple: true },
|
|
686
641
|
before: { type: "string" },
|
|
@@ -689,7 +644,6 @@ async function main() {
|
|
|
689
644
|
json: { type: "boolean", short: "j", default: false },
|
|
690
645
|
"api-token": { type: "string" },
|
|
691
646
|
"api-url": { type: "string" },
|
|
692
|
-
"memory-url": { type: "string" },
|
|
693
647
|
help: { type: "boolean", short: "h", default: false },
|
|
694
648
|
version: { type: "boolean", short: "v", default: false }
|
|
695
649
|
},
|
|
@@ -701,7 +655,7 @@ async function main() {
|
|
|
701
655
|
return;
|
|
702
656
|
}
|
|
703
657
|
if (values.version) {
|
|
704
|
-
console.log(`tfa-memory ${
|
|
658
|
+
console.log(`tfa-memory ${package_default.version}`);
|
|
705
659
|
return;
|
|
706
660
|
}
|
|
707
661
|
const [cmd, ...rest] = positionals;
|
|
@@ -709,19 +663,23 @@ async function main() {
|
|
|
709
663
|
printHelp();
|
|
710
664
|
process.exit(2);
|
|
711
665
|
}
|
|
666
|
+
if (["add", "ls", "rm", "delete", "del", "live", "sources"].includes(cmd))
|
|
667
|
+
die(`\`${cmd}\` was removed. ${MEMORY_FILES}`, 2);
|
|
712
668
|
if (cmd === "whoami")
|
|
713
669
|
process.exit(await whoami({
|
|
714
670
|
binName: "tfa-memory",
|
|
715
671
|
apiUrl: values["api-url"],
|
|
716
672
|
apiToken: values["api-token"]
|
|
717
673
|
}));
|
|
674
|
+
if (values.source !== undefined && values.source !== "todo")
|
|
675
|
+
die(`only past TODO messages are searchable. ${MEMORY_FILES}`, 2);
|
|
718
676
|
let ctx;
|
|
719
677
|
try {
|
|
720
678
|
const { apiUrl, apiToken } = resolveToken({
|
|
721
679
|
apiUrl: values["api-url"],
|
|
722
680
|
apiToken: values["api-token"]
|
|
723
681
|
});
|
|
724
|
-
ctx = { apiUrl,
|
|
682
|
+
ctx = { apiUrl, token: apiToken };
|
|
725
683
|
} catch (e) {
|
|
726
684
|
die(e?.message ?? String(e));
|
|
727
685
|
}
|
|
@@ -729,125 +687,38 @@ async function main() {
|
|
|
729
687
|
const limit = values.limit !== undefined ? Number(values.limit) : undefined;
|
|
730
688
|
if (limit !== undefined && (!Number.isInteger(limit) || limit < 1 || limit > 50))
|
|
731
689
|
die(`--limit must be an integer from 1 to 50`, 2);
|
|
732
|
-
const sources = values.source;
|
|
733
690
|
switch (cmd) {
|
|
734
|
-
case "add": {
|
|
735
|
-
let source = "manual", ref = new Date().toISOString().slice(0, 19), text;
|
|
736
|
-
if (rest.length >= 2 && rest[0].includes("/")) {
|
|
737
|
-
[source, ref] = splitAddr(rest[0]);
|
|
738
|
-
text = rest[1];
|
|
739
|
-
} else if (rest.length === 1) {
|
|
740
|
-
text = rest[0];
|
|
741
|
-
} else
|
|
742
|
-
die(`usage: tfa-memory add [source/ref] "text" | -`, 2);
|
|
743
|
-
if (text === "-")
|
|
744
|
-
text = readFileSync4(0, "utf8");
|
|
745
|
-
text = text.trim();
|
|
746
|
-
if (!text)
|
|
747
|
-
die("empty content");
|
|
748
|
-
let meta;
|
|
749
|
-
if (values.meta !== undefined) {
|
|
750
|
-
try {
|
|
751
|
-
meta = JSON.parse(values.meta);
|
|
752
|
-
} catch {
|
|
753
|
-
die(`--meta must be valid JSON`, 2);
|
|
754
|
-
}
|
|
755
|
-
if (typeof meta !== "object" || meta === null || Array.isArray(meta))
|
|
756
|
-
die(`--meta must be a JSON object`, 2);
|
|
757
|
-
}
|
|
758
|
-
const res = await api(ctx, "POST", "/memory", { source, ref, content: text, meta });
|
|
759
|
-
for (const w of res.warnings ?? [])
|
|
760
|
-
console.error(`Warning: ${w}`);
|
|
761
|
-
console.log(json ? JSON.stringify(res) : `${res.status} ${res.address}`);
|
|
762
|
-
break;
|
|
763
|
-
}
|
|
764
691
|
case "search": {
|
|
765
692
|
const query = rest[0] ?? die(`usage: tfa-memory search "query"`, 2);
|
|
766
|
-
const exclude = values.exclude?.join(",");
|
|
767
|
-
const res = await api(ctx,
|
|
768
|
-
|
|
769
|
-
console.error(`Warning: ${w}`);
|
|
770
|
-
if (res.lexicalCount !== undefined)
|
|
771
|
-
console.error(`${res.lexicalCount} row(s) contain a query word`);
|
|
772
|
-
printRows(res.rows, json);
|
|
773
|
-
break;
|
|
774
|
-
}
|
|
775
|
-
case "live": {
|
|
776
|
-
const budget = values.budget !== undefined ? Number(values.budget) : 1500;
|
|
777
|
-
if (!Number.isInteger(budget) || budget < 100 || budget > 20000)
|
|
778
|
-
die(`--budget must be an integer from 100 to 20000`, 2);
|
|
779
|
-
const res = await api(ctx, "GET", `/live/render${qs({ budget })}`);
|
|
780
|
-
const block = res.block ?? "";
|
|
781
|
-
if (json)
|
|
782
|
-
console.log(JSON.stringify({ block }));
|
|
783
|
-
else
|
|
784
|
-
process.stdout.write((block || "(empty live memory)") + `
|
|
785
|
-
`);
|
|
786
|
-
break;
|
|
787
|
-
}
|
|
788
|
-
case "ls": {
|
|
789
|
-
const res = await api(ctx, "GET", `/memory${qs({ sources, limit })}`);
|
|
790
|
-
printRows(res.rows, json);
|
|
693
|
+
const exclude = values.exclude?.map((a) => splitAddr(a).messageId).join(",");
|
|
694
|
+
const res = await api(ctx, `/messages/search${qs({ query, mode: values.mode, limit: limit ?? 5, todoId: values.todo, exclude })}`);
|
|
695
|
+
printRows(res.hits.map((h) => ({ address: `todo/${h.todoId}:${h.messageId}`, createdAt: h.createdAt, content: h.snippet, meta: { projectId: h.projectId, role: h.role } })), json);
|
|
791
696
|
break;
|
|
792
697
|
}
|
|
793
698
|
case "get": {
|
|
794
|
-
const
|
|
795
|
-
const
|
|
699
|
+
const addr = rest[0] ?? die("address required (todo/<todoId>:<messageId>)", 2);
|
|
700
|
+
const [row] = (await around(ctx, addr, "0", "0")).rows;
|
|
796
701
|
if (json)
|
|
797
|
-
console.log(JSON.stringify(
|
|
702
|
+
console.log(JSON.stringify(row, null, 2));
|
|
798
703
|
else
|
|
799
|
-
process.stdout.write(
|
|
704
|
+
process.stdout.write(row.content + `
|
|
800
705
|
`);
|
|
801
706
|
break;
|
|
802
707
|
}
|
|
803
708
|
case "around": {
|
|
804
|
-
const
|
|
805
|
-
|
|
806
|
-
if (res.total !== undefined)
|
|
807
|
-
console.error(`message ${res.index + 1} of ${res.total}`);
|
|
709
|
+
const res = await around(ctx, rest[0] ?? die("address required (todo/<todoId>:<messageId>)", 2), values.before, values.after);
|
|
710
|
+
console.error(`message ${res.index + 1} of ${res.total}`);
|
|
808
711
|
printRows(res.rows, json, 2000);
|
|
809
712
|
break;
|
|
810
713
|
}
|
|
811
|
-
case "rm":
|
|
812
|
-
case "delete":
|
|
813
|
-
case "del": {
|
|
814
|
-
let body;
|
|
815
|
-
if (rest[0]) {
|
|
816
|
-
const [source, ref] = splitAddr(rest[0]);
|
|
817
|
-
body = { source, ref };
|
|
818
|
-
} else if (sources && !sources.includes(","))
|
|
819
|
-
body = { source: sources };
|
|
820
|
-
else
|
|
821
|
-
die("usage: tfa-memory rm <source/ref> | tfa-memory rm --source <name>", 2);
|
|
822
|
-
const res = await api(ctx, "POST", "/memory/remove", body);
|
|
823
|
-
console.log(json ? JSON.stringify(res) : `removed ${res.removed}`);
|
|
824
|
-
break;
|
|
825
|
-
}
|
|
826
|
-
case "sources": {
|
|
827
|
-
const res = await api(ctx, "GET", "/memory/sources");
|
|
828
|
-
if (json) {
|
|
829
|
-
console.log(JSON.stringify(res.sources, null, 2));
|
|
830
|
-
break;
|
|
831
|
-
}
|
|
832
|
-
if (!res.sources.length) {
|
|
833
|
-
console.log("(no sources)");
|
|
834
|
-
break;
|
|
835
|
-
}
|
|
836
|
-
const w = Math.max(...res.sources.map((s) => s.source.length));
|
|
837
|
-
for (const s of res.sources.sort((a, b) => (b.lastWrite ?? Infinity) - (a.lastWrite ?? Infinity)))
|
|
838
|
-
console.log(`${s.source.padEnd(w)} ${String(s.count ?? "live").padStart(4)} ${s.lastWrite ? new Date(s.lastWrite).toISOString().slice(0, 16) : "live"}`);
|
|
839
|
-
break;
|
|
840
|
-
}
|
|
841
714
|
default:
|
|
842
715
|
die(`unknown command: ${cmd} (try 'tfa-memory --help')`, 2);
|
|
843
716
|
}
|
|
844
717
|
}
|
|
845
|
-
var VERSION = package_default.version;
|
|
846
718
|
if (isEntrypoint) {
|
|
847
719
|
checkForUpdates({ name: package_default.name, version: package_default.version });
|
|
848
720
|
main();
|
|
849
721
|
}
|
|
850
722
|
export {
|
|
851
|
-
|
|
852
|
-
deriveMemoryUrl
|
|
723
|
+
splitAddr
|
|
853
724
|
};
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@todoforai/tfa-memory",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Zero-config TODOforAI
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Zero-config TODOforAI CLI to search past TODO conversations (full-text and semantic), print one message or read the conversation around it. Read-only; memory itself lives in project files. Reuses bridge credentials.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"tfa-memory": "dist/cli.js"
|