@membank/cli 0.16.1 → 0.17.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.
Files changed (2) hide show
  1. package/dist/index.mjs +202 -168
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -4,17 +4,180 @@ import { ACTIVITY_EVENT_TYPE_VALUES, ActivityEventTypeSchema, DatabaseManager, E
4
4
  import { runExtraction, runSynthesis, startServer } from "@membank/mcp";
5
5
  import chalk from "chalk";
6
6
  import { Command } from "commander";
7
+ import Table from "cli-table3";
7
8
  import ora from "ora";
8
9
  import { z } from "zod";
9
10
  import { existsSync, mkdirSync, mkdtempSync, readFileSync, renameSync, writeFileSync } from "node:fs";
10
11
  import { homedir, tmpdir } from "node:os";
11
12
  import { dirname, join } from "node:path";
12
13
  import { diffLines } from "@membank/core/client";
13
- import Table from "cli-table3";
14
14
  import { execFile } from "node:child_process";
15
15
  import { promisify } from "node:util";
16
16
  import { createInterface } from "node:readline";
17
+ //#region src/formatter.ts
18
+ const TYPE_COLORS = {
19
+ correction: chalk.yellow,
20
+ preference: chalk.cyan,
21
+ decision: chalk.blue,
22
+ learning: chalk.green,
23
+ fact: chalk.dim
24
+ };
25
+ function colorType(type) {
26
+ return TYPE_COLORS[type](type);
27
+ }
28
+ function statsRow(label, value, warn) {
29
+ if (warn) process.stdout.write(` ${chalk.yellow("⚠")} ${label.padEnd(12)} ${value}\n`);
30
+ else process.stdout.write(` ${" ".concat(label).padEnd(14)} ${value}\n`);
31
+ }
32
+ function truncate$2(str, max) {
33
+ return str.length > max ? `${str.slice(0, max - 1)}…` : str;
34
+ }
35
+ var Formatter = class Formatter {
36
+ #isJson;
37
+ constructor(isJson) {
38
+ this.#isJson = isJson;
39
+ }
40
+ static create(forceJson = false) {
41
+ return new Formatter(forceJson || !process.stdout.isTTY);
42
+ }
43
+ get isJson() {
44
+ return this.#isJson;
45
+ }
46
+ outputMemory(memory) {
47
+ if (this.#isJson) {
48
+ process.stdout.write(`${JSON.stringify(memory)}\n`);
49
+ return;
50
+ }
51
+ const tags = memory.tags.length > 0 ? memory.tags.join(", ") : "(none)";
52
+ process.stdout.write("\n");
53
+ process.stdout.write(` ${colorType(memory.type)} ${chalk.dim(memory.id)}\n`);
54
+ process.stdout.write(` ${memory.content}\n`);
55
+ const scope = memory.projects.length > 0 ? memory.projects.map((p) => p.name).join(", ") : "global";
56
+ process.stdout.write(` ${chalk.dim("Tags:")} ${tags} ${chalk.dim("Project:")} ${scope}\n`);
57
+ process.stdout.write(`\n ${chalk.dim(`Hint: pin with membank pin ${memory.id}`)}\n\n`);
58
+ }
59
+ outputMemories(memories) {
60
+ if (this.#isJson) {
61
+ process.stdout.write(`${JSON.stringify(memories)}\n`);
62
+ return;
63
+ }
64
+ if (memories.length === 0) {
65
+ process.stdout.write(`${chalk.dim("No memories found.")}\n`);
66
+ return;
67
+ }
68
+ const table = new Table({
69
+ head: [
70
+ "Type",
71
+ "ID",
72
+ "Content",
73
+ "Pinned"
74
+ ].map((h) => chalk.bold(h)),
75
+ style: {
76
+ head: [],
77
+ border: []
78
+ }
79
+ });
80
+ for (const m of memories) {
81
+ const tags = m.tags.length > 0 ? m.tags.join(", ") : "(none)";
82
+ const mScope = m.projects.length > 0 ? m.projects.map((p) => p.name).join(", ") : "global";
83
+ const meta = `${truncate$2(m.content, 45)}\n${chalk.dim(`${tags} · ${mScope}`)}`;
84
+ table.push([
85
+ colorType(m.type),
86
+ chalk.dim(m.id),
87
+ meta,
88
+ m.pinned ? "📌" : ""
89
+ ]);
90
+ }
91
+ process.stdout.write(`\n${table.toString()}\n\n`);
92
+ }
93
+ outputStats(stats) {
94
+ if (this.#isJson) {
95
+ process.stdout.write(`${JSON.stringify(stats)}\n`);
96
+ return;
97
+ }
98
+ const types = [
99
+ "correction",
100
+ "preference",
101
+ "decision",
102
+ "learning",
103
+ "fact"
104
+ ];
105
+ process.stdout.write("\n");
106
+ for (const type of types) process.stdout.write(` ${TYPE_COLORS[type](type.padEnd(14))} ${stats.byType[type]}\n`);
107
+ process.stdout.write(`\n ${chalk.dim("─".repeat(24))}\n`);
108
+ process.stdout.write(` ${"total".padEnd(14)} ${stats.total}\n`);
109
+ statsRow("needs_review", String(stats.needsReview), stats.needsReview > 0);
110
+ statsRow("pin_budget", `${stats.pinBudgetChars} / ${PIN_BUDGET_THRESHOLD} chars`, stats.pinBudgetChars > PIN_BUDGET_THRESHOLD);
111
+ process.stdout.write("\n");
112
+ }
113
+ outputQueryResults(results) {
114
+ if (this.#isJson) {
115
+ process.stdout.write(`${JSON.stringify(results)}\n`);
116
+ return;
117
+ }
118
+ if (results.length === 0) {
119
+ process.stdout.write(`${chalk.dim("No memories found.")}\n`);
120
+ return;
121
+ }
122
+ const table = new Table({
123
+ head: [
124
+ "Type",
125
+ "ID",
126
+ "Content",
127
+ "Score"
128
+ ].map((h) => chalk.bold(h)),
129
+ style: {
130
+ head: [],
131
+ border: []
132
+ }
133
+ });
134
+ for (const r of results) {
135
+ const scoreStr = r.score.toFixed(4);
136
+ const score = r.score >= .85 ? chalk.bold(scoreStr) : r.score < .75 ? chalk.dim(scoreStr) : scoreStr;
137
+ const tags = r.tags.length > 0 ? r.tags.join(", ") : "(none)";
138
+ const rScope = r.projects.length > 0 ? r.projects.map((p) => p.name).join(", ") : "global";
139
+ const meta = `${truncate$2(r.content, 45)}\n${chalk.dim(`${tags} · ${rScope}`)}`;
140
+ table.push([
141
+ colorType(r.type),
142
+ chalk.dim(r.id),
143
+ meta,
144
+ score
145
+ ]);
146
+ }
147
+ process.stdout.write(`\n${table.toString()}\n\n`);
148
+ }
149
+ outputReview(memories) {
150
+ if (this.#isJson) {
151
+ process.stdout.write(`${JSON.stringify(memories)}\n`);
152
+ return;
153
+ }
154
+ if (memories.length === 0) {
155
+ process.stdout.write(`${chalk.dim("No memories flagged for review.")}\n`);
156
+ return;
157
+ }
158
+ for (const m of memories) {
159
+ process.stdout.write("\n");
160
+ process.stdout.write(` ${colorType(m.type)} ${chalk.dim(m.id)}\n`);
161
+ process.stdout.write(` ${truncate$2(m.content, 80)}\n`);
162
+ for (const event of m.reviewEvents) this.#outputReviewEvent(event);
163
+ }
164
+ process.stdout.write("\n");
165
+ }
166
+ #outputReviewEvent(event) {
167
+ const pct = `${Math.round(event.similarity * 100)}%`;
168
+ const conflictRef = event.conflictingMemoryId ? chalk.dim(event.conflictingMemoryId) : chalk.dim("(deleted)");
169
+ const ts = new Date(event.createdAt).toLocaleString();
170
+ process.stdout.write(` ${chalk.yellow("⚠")} ${pct} similarity conflict: ${conflictRef} ${chalk.dim(ts)}\n`);
171
+ if (event.conflictContentSnapshot) process.stdout.write(` ${chalk.dim("snapshot:")} ${truncate$2(event.conflictContentSnapshot, 60)}\n`);
172
+ }
173
+ error(msg) {
174
+ if (this.#isJson) process.stderr.write(`${JSON.stringify({ error: msg })}\n`);
175
+ else process.stderr.write(`${chalk.red("Error:")} ${msg}\n`);
176
+ }
177
+ };
178
+ //#endregion
17
179
  //#region src/commands/activity.ts
180
+ const SNIPPET_LENGTH = 120;
18
181
  const EVENT_COLORS = {
19
182
  "memory.created": chalk.green,
20
183
  "memory.updated": chalk.cyan,
@@ -22,8 +185,41 @@ const EVENT_COLORS = {
22
185
  "memory.flagged": chalk.yellow,
23
186
  "memory.queried": chalk.dim
24
187
  };
188
+ function formatEventDetail(event) {
189
+ const p = event.payload;
190
+ const getString = (key) => typeof p[key] === "string" ? p[key] : void 0;
191
+ const getNumber = (key) => typeof p[key] === "number" ? p[key] : void 0;
192
+ switch (event.eventType) {
193
+ case "memory.created":
194
+ case "memory.updated": {
195
+ const snippet = getString("contentSnapshot");
196
+ if (snippet === void 0) return null;
197
+ return chalk.dim(` ${truncate$2(snippet, SNIPPET_LENGTH).replace(/\n/g, " ↵ ")}`);
198
+ }
199
+ case "memory.deleted": {
200
+ const snippet = getString("contentSnapshot");
201
+ if (snippet === void 0) return null;
202
+ return chalk.dim(` (deleted) ${truncate$2(snippet, SNIPPET_LENGTH).replace(/\n/g, " ↵ ")}`);
203
+ }
204
+ case "memory.queried": {
205
+ const query = getString("query");
206
+ if (query === void 0) return null;
207
+ return chalk.dim(` "${truncate$2(query, SNIPPET_LENGTH)}"`);
208
+ }
209
+ case "memory.flagged": {
210
+ const sim = getNumber("similarity");
211
+ const conflictId = getString("conflictingMemoryId");
212
+ if (sim === void 0) return null;
213
+ const pct = `${Math.round(sim * 100)}% similar`;
214
+ const conflict = conflictId !== void 0 ? ` · conflict: ${conflictId.slice(0, 8)}` : "";
215
+ return chalk.dim(` ${pct}${conflict}`);
216
+ }
217
+ }
218
+ }
25
219
  function formatEvent(event) {
26
- return ` ${(EVENT_COLORS[event.eventType] ?? chalk.white)(event.eventType.padEnd(16))} ${new Date(event.createdAt).toLocaleTimeString()}${event.memoryId !== null ? chalk.dim(` [${event.memoryId.slice(0, 8)}]`) : ""}`;
220
+ const header = ` ${(EVENT_COLORS[event.eventType] ?? chalk.white)(event.eventType.padEnd(16))} ${new Date(event.createdAt).toLocaleTimeString()}${event.memoryId !== null ? chalk.dim(` [${event.memoryId.slice(0, 8)}]`) : ""}`;
221
+ const detail = formatEventDetail(event);
222
+ return detail !== null ? `${header}\n${detail}` : header;
27
223
  }
28
224
  async function activityCommand(options, formatter) {
29
225
  const db = DatabaseManager.open();
@@ -499,7 +695,7 @@ function memoryDiffCommand(id, v1, v2, db, formatter) {
499
695
  }
500
696
  //#endregion
501
697
  //#region src/commands/memory/history.ts
502
- function truncate$2(str, max) {
698
+ function truncate$1(str, max) {
503
699
  return str.length > max ? `${str.slice(0, max - 1)}…` : str;
504
700
  }
505
701
  function memoryHistoryCommand(id, db, formatter) {
@@ -523,7 +719,7 @@ function memoryHistoryCommand(id, db, formatter) {
523
719
  for (const v of versions) table.push([
524
720
  String(v.version),
525
721
  v.createdAt,
526
- truncate$2(v.content.replace(/\n/g, " "), 60)
722
+ truncate$1(v.content.replace(/\n/g, " "), 60)
527
723
  ]);
528
724
  process.stdout.write(`${table.toString()}\n`);
529
725
  }
@@ -711,7 +907,7 @@ function synthesizeDiffCommand(v1, v2, opts, formatter) {
711
907
  }
712
908
  //#endregion
713
909
  //#region src/commands/synthesize/history.ts
714
- function truncate$1(str, max) {
910
+ function truncate(str, max) {
715
911
  return str.length > max ? `${str.slice(0, max - 1)}…` : str;
716
912
  }
717
913
  function synthesizeHistoryCommand(opts, formatter) {
@@ -739,7 +935,7 @@ function synthesizeHistoryCommand(opts, formatter) {
739
935
  for (const v of versions) table.push([
740
936
  String(v.version),
741
937
  new Date(v.synthesizedAt).toLocaleString(),
742
- truncate$1(v.content.replace(/\n/g, " "), 60)
938
+ truncate(v.content.replace(/\n/g, " "), 60)
743
939
  ]);
744
940
  process.stdout.write(`${table.toString()}\n`);
745
941
  } finally {
@@ -858,168 +1054,6 @@ function unpinCommand(id, db) {
858
1054
  }
859
1055
  }
860
1056
  //#endregion
861
- //#region src/formatter.ts
862
- const TYPE_COLORS = {
863
- correction: chalk.yellow,
864
- preference: chalk.cyan,
865
- decision: chalk.blue,
866
- learning: chalk.green,
867
- fact: chalk.dim
868
- };
869
- function colorType(type) {
870
- return TYPE_COLORS[type](type);
871
- }
872
- function statsRow(label, value, warn) {
873
- if (warn) process.stdout.write(` ${chalk.yellow("⚠")} ${label.padEnd(12)} ${value}\n`);
874
- else process.stdout.write(` ${" ".concat(label).padEnd(14)} ${value}\n`);
875
- }
876
- function truncate(str, max) {
877
- return str.length > max ? `${str.slice(0, max - 1)}…` : str;
878
- }
879
- var Formatter = class Formatter {
880
- #isJson;
881
- constructor(isJson) {
882
- this.#isJson = isJson;
883
- }
884
- static create(forceJson = false) {
885
- return new Formatter(forceJson || !process.stdout.isTTY);
886
- }
887
- get isJson() {
888
- return this.#isJson;
889
- }
890
- outputMemory(memory) {
891
- if (this.#isJson) {
892
- process.stdout.write(`${JSON.stringify(memory)}\n`);
893
- return;
894
- }
895
- const tags = memory.tags.length > 0 ? memory.tags.join(", ") : "(none)";
896
- process.stdout.write("\n");
897
- process.stdout.write(` ${colorType(memory.type)} ${chalk.dim(memory.id)}\n`);
898
- process.stdout.write(` ${memory.content}\n`);
899
- const scope = memory.projects.length > 0 ? memory.projects.map((p) => p.name).join(", ") : "global";
900
- process.stdout.write(` ${chalk.dim("Tags:")} ${tags} ${chalk.dim("Project:")} ${scope}\n`);
901
- process.stdout.write(`\n ${chalk.dim(`Hint: pin with membank pin ${memory.id}`)}\n\n`);
902
- }
903
- outputMemories(memories) {
904
- if (this.#isJson) {
905
- process.stdout.write(`${JSON.stringify(memories)}\n`);
906
- return;
907
- }
908
- if (memories.length === 0) {
909
- process.stdout.write(`${chalk.dim("No memories found.")}\n`);
910
- return;
911
- }
912
- const table = new Table({
913
- head: [
914
- "Type",
915
- "ID",
916
- "Content",
917
- "Pinned"
918
- ].map((h) => chalk.bold(h)),
919
- style: {
920
- head: [],
921
- border: []
922
- }
923
- });
924
- for (const m of memories) {
925
- const tags = m.tags.length > 0 ? m.tags.join(", ") : "(none)";
926
- const mScope = m.projects.length > 0 ? m.projects.map((p) => p.name).join(", ") : "global";
927
- const meta = `${truncate(m.content, 45)}\n${chalk.dim(`${tags} · ${mScope}`)}`;
928
- table.push([
929
- colorType(m.type),
930
- chalk.dim(m.id),
931
- meta,
932
- m.pinned ? "📌" : ""
933
- ]);
934
- }
935
- process.stdout.write(`\n${table.toString()}\n\n`);
936
- }
937
- outputStats(stats) {
938
- if (this.#isJson) {
939
- process.stdout.write(`${JSON.stringify(stats)}\n`);
940
- return;
941
- }
942
- const types = [
943
- "correction",
944
- "preference",
945
- "decision",
946
- "learning",
947
- "fact"
948
- ];
949
- process.stdout.write("\n");
950
- for (const type of types) process.stdout.write(` ${TYPE_COLORS[type](type.padEnd(14))} ${stats.byType[type]}\n`);
951
- process.stdout.write(`\n ${chalk.dim("─".repeat(24))}\n`);
952
- process.stdout.write(` ${"total".padEnd(14)} ${stats.total}\n`);
953
- statsRow("needs_review", String(stats.needsReview), stats.needsReview > 0);
954
- statsRow("pin_budget", `${stats.pinBudgetChars} / ${PIN_BUDGET_THRESHOLD} chars`, stats.pinBudgetChars > PIN_BUDGET_THRESHOLD);
955
- process.stdout.write("\n");
956
- }
957
- outputQueryResults(results) {
958
- if (this.#isJson) {
959
- process.stdout.write(`${JSON.stringify(results)}\n`);
960
- return;
961
- }
962
- if (results.length === 0) {
963
- process.stdout.write(`${chalk.dim("No memories found.")}\n`);
964
- return;
965
- }
966
- const table = new Table({
967
- head: [
968
- "Type",
969
- "ID",
970
- "Content",
971
- "Score"
972
- ].map((h) => chalk.bold(h)),
973
- style: {
974
- head: [],
975
- border: []
976
- }
977
- });
978
- for (const r of results) {
979
- const scoreStr = r.score.toFixed(4);
980
- const score = r.score >= .85 ? chalk.bold(scoreStr) : r.score < .75 ? chalk.dim(scoreStr) : scoreStr;
981
- const tags = r.tags.length > 0 ? r.tags.join(", ") : "(none)";
982
- const rScope = r.projects.length > 0 ? r.projects.map((p) => p.name).join(", ") : "global";
983
- const meta = `${truncate(r.content, 45)}\n${chalk.dim(`${tags} · ${rScope}`)}`;
984
- table.push([
985
- colorType(r.type),
986
- chalk.dim(r.id),
987
- meta,
988
- score
989
- ]);
990
- }
991
- process.stdout.write(`\n${table.toString()}\n\n`);
992
- }
993
- outputReview(memories) {
994
- if (this.#isJson) {
995
- process.stdout.write(`${JSON.stringify(memories)}\n`);
996
- return;
997
- }
998
- if (memories.length === 0) {
999
- process.stdout.write(`${chalk.dim("No memories flagged for review.")}\n`);
1000
- return;
1001
- }
1002
- for (const m of memories) {
1003
- process.stdout.write("\n");
1004
- process.stdout.write(` ${colorType(m.type)} ${chalk.dim(m.id)}\n`);
1005
- process.stdout.write(` ${truncate(m.content, 80)}\n`);
1006
- for (const event of m.reviewEvents) this.#outputReviewEvent(event);
1007
- }
1008
- process.stdout.write("\n");
1009
- }
1010
- #outputReviewEvent(event) {
1011
- const pct = `${Math.round(event.similarity * 100)}%`;
1012
- const conflictRef = event.conflictingMemoryId ? chalk.dim(event.conflictingMemoryId) : chalk.dim("(deleted)");
1013
- const ts = new Date(event.createdAt).toLocaleString();
1014
- process.stdout.write(` ${chalk.yellow("⚠")} ${pct} similarity conflict: ${conflictRef} ${chalk.dim(ts)}\n`);
1015
- if (event.conflictContentSnapshot) process.stdout.write(` ${chalk.dim("snapshot:")} ${truncate(event.conflictContentSnapshot, 60)}\n`);
1016
- }
1017
- error(msg) {
1018
- if (this.#isJson) process.stderr.write(`${JSON.stringify({ error: msg })}\n`);
1019
- else process.stderr.write(`${chalk.red("Error:")} ${msg}\n`);
1020
- }
1021
- };
1022
- //#endregion
1023
1057
  //#region src/prompt-helper.ts
1024
1058
  var PromptHelper = class {
1025
1059
  autoConfirm;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@membank/cli",
3
- "version": "0.16.1",
3
+ "version": "0.17.0",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -21,8 +21,8 @@
21
21
  "commander": "^14.0.3",
22
22
  "ora": "^9.4.0",
23
23
  "zod": "^4.4.3",
24
- "@membank/core": "0.14.1",
25
- "@membank/mcp": "0.16.1"
24
+ "@membank/core": "0.15.0",
25
+ "@membank/mcp": "0.16.2"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^25.6.0",