@danielsimonjr/memoryjs 2.1.0 → 2.1.2
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 +1 -1
- package/dist/cli/index.js +356 -3
- package/dist/cli/index.js.map +1 -1
- package/dist/index.cjs +5 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# MemoryJS
|
|
2
2
|
|
|
3
|
-
[](https://github.com/danielsimonjr/memoryjs)
|
|
4
4
|
[](https://www.npmjs.com/package/@danielsimonjr/memoryjs)
|
|
5
5
|
[](LICENSE)
|
|
6
6
|
[](https://www.typescriptlang.org/)
|
package/dist/cli/index.js
CHANGED
|
@@ -1191,7 +1191,11 @@ var init_schemas = __esm({
|
|
|
1191
1191
|
recordedAt: z.string().optional()
|
|
1192
1192
|
})).optional(),
|
|
1193
1193
|
lifecycleStatus: z.enum(["draft", "published", "archived"]).optional()
|
|
1194
|
-
|
|
1194
|
+
// v2.1.1: subclass managers (DecisionManager, HeuristicManager, ProjectContextManager,
|
|
1195
|
+
// ToolAffordanceManager, ExclusionManager, ObservationDedupManager) attach domain-specific
|
|
1196
|
+
// record fields (decisionRecord, projectContext, heuristic, etc.) and a lastModified
|
|
1197
|
+
// timestamp via updateEntity(). .passthrough() admits those without per-field enumeration.
|
|
1198
|
+
}).passthrough();
|
|
1195
1199
|
RelationSchema = z.object({
|
|
1196
1200
|
from: entityNameSchema,
|
|
1197
1201
|
to: entityNameSchema,
|
|
@@ -36892,6 +36896,354 @@ function registerToolAffordanceCommands(program2) {
|
|
|
36892
36896
|
});
|
|
36893
36897
|
}
|
|
36894
36898
|
|
|
36899
|
+
// src/cli/commands/smoke.ts
|
|
36900
|
+
init_esm_shims();
|
|
36901
|
+
init_ManagerContext();
|
|
36902
|
+
import { promises as fs17 } from "fs";
|
|
36903
|
+
import { join as join8 } from "path";
|
|
36904
|
+
import { tmpdir } from "os";
|
|
36905
|
+
import { performance } from "perf_hooks";
|
|
36906
|
+
async function runSteps(steps, verbose) {
|
|
36907
|
+
const results = [];
|
|
36908
|
+
for (const step of steps) {
|
|
36909
|
+
const start = performance.now();
|
|
36910
|
+
try {
|
|
36911
|
+
await step.run();
|
|
36912
|
+
const durationMs = performance.now() - start;
|
|
36913
|
+
results.push({ name: step.name, ok: true, durationMs });
|
|
36914
|
+
if (verbose) {
|
|
36915
|
+
console.log(` ${formatSuccess("\u2713")} ${step.name} (${durationMs.toFixed(1)} ms)`);
|
|
36916
|
+
}
|
|
36917
|
+
} catch (error) {
|
|
36918
|
+
const durationMs = performance.now() - start;
|
|
36919
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
36920
|
+
results.push({ name: step.name, ok: false, durationMs, error: message });
|
|
36921
|
+
if (verbose) {
|
|
36922
|
+
console.log(` ${formatError("\u2717")} ${step.name} (${durationMs.toFixed(1)} ms) \u2014 ${message}`);
|
|
36923
|
+
}
|
|
36924
|
+
}
|
|
36925
|
+
}
|
|
36926
|
+
return results;
|
|
36927
|
+
}
|
|
36928
|
+
function buildSteps(ctx) {
|
|
36929
|
+
return [
|
|
36930
|
+
// ---- Entity (CRUD) ----
|
|
36931
|
+
{
|
|
36932
|
+
name: "entity:create",
|
|
36933
|
+
run: async () => {
|
|
36934
|
+
const [created] = await ctx.entityManager.createEntities([{
|
|
36935
|
+
name: "Alpha",
|
|
36936
|
+
entityType: "service",
|
|
36937
|
+
observations: ["root node"]
|
|
36938
|
+
}]);
|
|
36939
|
+
if (created.name !== "Alpha") throw new Error(`expected Alpha got ${created.name}`);
|
|
36940
|
+
}
|
|
36941
|
+
},
|
|
36942
|
+
{
|
|
36943
|
+
name: "entity:open_nodes",
|
|
36944
|
+
run: async () => {
|
|
36945
|
+
const result = await ctx.searchManager.openNodes(["Alpha"]);
|
|
36946
|
+
if (result.entities.length !== 1) throw new Error(`expected 1 entity got ${result.entities.length}`);
|
|
36947
|
+
}
|
|
36948
|
+
},
|
|
36949
|
+
{
|
|
36950
|
+
name: "entity:update",
|
|
36951
|
+
run: async () => {
|
|
36952
|
+
await ctx.entityManager.updateEntity("Alpha", { importance: 7 });
|
|
36953
|
+
const after = ctx.storage.getEntityByName("Alpha");
|
|
36954
|
+
if (after?.importance !== 7) throw new Error(`importance not persisted: ${after?.importance}`);
|
|
36955
|
+
}
|
|
36956
|
+
},
|
|
36957
|
+
// ---- Relation ----
|
|
36958
|
+
{
|
|
36959
|
+
name: "relation:create_endpoints",
|
|
36960
|
+
run: async () => {
|
|
36961
|
+
await ctx.entityManager.createEntities([
|
|
36962
|
+
{ name: "Beta", entityType: "service", observations: ["second node"] },
|
|
36963
|
+
{ name: "Gamma", entityType: "service", observations: ["third node"] }
|
|
36964
|
+
]);
|
|
36965
|
+
}
|
|
36966
|
+
},
|
|
36967
|
+
{
|
|
36968
|
+
name: "relation:create",
|
|
36969
|
+
run: async () => {
|
|
36970
|
+
await ctx.relationManager.createRelations([{ from: "Alpha", to: "Beta", relationType: "depends_on" }]);
|
|
36971
|
+
const graph = await ctx.storage.loadGraph();
|
|
36972
|
+
if (!graph.relations.some((r) => r.from === "Alpha" && r.to === "Beta")) {
|
|
36973
|
+
throw new Error("relation Alpha\u2192Beta not found in graph");
|
|
36974
|
+
}
|
|
36975
|
+
}
|
|
36976
|
+
},
|
|
36977
|
+
// ---- Observation ----
|
|
36978
|
+
{
|
|
36979
|
+
name: "observation:add",
|
|
36980
|
+
run: async () => {
|
|
36981
|
+
await ctx.observationManager.addObservations([
|
|
36982
|
+
{ entityName: "Alpha", contents: ["extra note for search"] }
|
|
36983
|
+
]);
|
|
36984
|
+
const obs = await ctx.observationManager.getObservationsFor("Alpha");
|
|
36985
|
+
if (!obs.some((o) => o.includes("extra note"))) throw new Error("observation not persisted");
|
|
36986
|
+
}
|
|
36987
|
+
},
|
|
36988
|
+
{
|
|
36989
|
+
name: "observation:delete",
|
|
36990
|
+
run: async () => {
|
|
36991
|
+
await ctx.observationManager.deleteObservations([
|
|
36992
|
+
{ entityName: "Alpha", observations: ["root node"] }
|
|
36993
|
+
]);
|
|
36994
|
+
const obs = await ctx.observationManager.getObservationsFor("Alpha");
|
|
36995
|
+
if (obs.includes("root node")) throw new Error("observation not deleted");
|
|
36996
|
+
}
|
|
36997
|
+
},
|
|
36998
|
+
// ---- Search ----
|
|
36999
|
+
{
|
|
37000
|
+
name: "search:basic",
|
|
37001
|
+
run: async () => {
|
|
37002
|
+
const result = await ctx.searchManager.searchNodes("extra note");
|
|
37003
|
+
if (result.entities.length === 0) throw new Error("basic search returned no entities");
|
|
37004
|
+
}
|
|
37005
|
+
},
|
|
37006
|
+
{
|
|
37007
|
+
name: "search:boolean",
|
|
37008
|
+
run: async () => {
|
|
37009
|
+
const result = await ctx.searchManager.booleanSearch("extra AND note");
|
|
37010
|
+
if (result.entities.length === 0) throw new Error("boolean search returned no entities");
|
|
37011
|
+
}
|
|
37012
|
+
},
|
|
37013
|
+
{
|
|
37014
|
+
name: "search:ranked",
|
|
37015
|
+
run: async () => {
|
|
37016
|
+
const result = await ctx.rankedSearch.searchNodesRanked("extra note", void 0, void 0, void 0, 5);
|
|
37017
|
+
if (result.length === 0) throw new Error("ranked search returned no results");
|
|
37018
|
+
}
|
|
37019
|
+
},
|
|
37020
|
+
// ---- Tag ----
|
|
37021
|
+
{
|
|
37022
|
+
name: "tag:add",
|
|
37023
|
+
run: async () => {
|
|
37024
|
+
await ctx.entityManager.addTags("Alpha", ["critical", "core"]);
|
|
37025
|
+
const e = ctx.storage.getEntityByName("Alpha");
|
|
37026
|
+
if (!e?.tags?.includes("critical")) throw new Error("tag not added");
|
|
37027
|
+
}
|
|
37028
|
+
},
|
|
37029
|
+
{
|
|
37030
|
+
name: "tag:remove",
|
|
37031
|
+
run: async () => {
|
|
37032
|
+
await ctx.entityManager.removeTags("Alpha", ["core"]);
|
|
37033
|
+
const e = ctx.storage.getEntityByName("Alpha");
|
|
37034
|
+
if (e?.tags?.includes("core")) throw new Error("tag not removed");
|
|
37035
|
+
}
|
|
37036
|
+
},
|
|
37037
|
+
// ---- Hierarchy ----
|
|
37038
|
+
{
|
|
37039
|
+
name: "hierarchy:set_parent",
|
|
37040
|
+
run: async () => {
|
|
37041
|
+
await ctx.hierarchyManager.setEntityParent("Beta", "Alpha");
|
|
37042
|
+
const e = ctx.storage.getEntityByName("Beta");
|
|
37043
|
+
if (e?.parentId !== "Alpha") throw new Error(`parent not set: ${e?.parentId}`);
|
|
37044
|
+
}
|
|
37045
|
+
},
|
|
37046
|
+
{
|
|
37047
|
+
name: "hierarchy:ancestors",
|
|
37048
|
+
run: async () => {
|
|
37049
|
+
const ancestors = await ctx.hierarchyManager.getAncestors("Beta");
|
|
37050
|
+
if (!ancestors.some((a) => a.name === "Alpha")) throw new Error("Alpha not in Beta ancestors");
|
|
37051
|
+
}
|
|
37052
|
+
},
|
|
37053
|
+
// ---- Graph algorithms ----
|
|
37054
|
+
{
|
|
37055
|
+
name: "graph:shortest_path",
|
|
37056
|
+
run: async () => {
|
|
37057
|
+
const result = await ctx.graphTraversal.findShortestPath("Alpha", "Beta");
|
|
37058
|
+
if (!result || result.path.length === 0) throw new Error("no path found Alpha\u2192Beta");
|
|
37059
|
+
}
|
|
37060
|
+
},
|
|
37061
|
+
{
|
|
37062
|
+
name: "graph:connected_components",
|
|
37063
|
+
run: async () => {
|
|
37064
|
+
const result = await ctx.graphTraversal.findConnectedComponents();
|
|
37065
|
+
if (result.components.length === 0) throw new Error("no connected components");
|
|
37066
|
+
}
|
|
37067
|
+
},
|
|
37068
|
+
// ---- IO ----
|
|
37069
|
+
{
|
|
37070
|
+
name: "io:export_json",
|
|
37071
|
+
run: async () => {
|
|
37072
|
+
const graph = await ctx.storage.loadGraph();
|
|
37073
|
+
const exported = ctx.ioManager.exportGraph(graph, "json");
|
|
37074
|
+
const parsed = JSON.parse(exported);
|
|
37075
|
+
if (!Array.isArray(parsed.entities) || parsed.entities.length < 3) {
|
|
37076
|
+
throw new Error(`export json had ${parsed.entities?.length} entities`);
|
|
37077
|
+
}
|
|
37078
|
+
}
|
|
37079
|
+
},
|
|
37080
|
+
// ---- Maintenance / Analytics ----
|
|
37081
|
+
{
|
|
37082
|
+
name: "analytics:stats",
|
|
37083
|
+
run: async () => {
|
|
37084
|
+
const stats = await ctx.analyticsManager.getGraphStats();
|
|
37085
|
+
if (stats.totalEntities < 3) throw new Error(`stats.totalEntities=${stats.totalEntities}`);
|
|
37086
|
+
}
|
|
37087
|
+
},
|
|
37088
|
+
{
|
|
37089
|
+
name: "maintenance:validate",
|
|
37090
|
+
run: async () => {
|
|
37091
|
+
const report = await ctx.analyticsManager.validateGraph();
|
|
37092
|
+
if (!report) throw new Error("validate returned nothing");
|
|
37093
|
+
}
|
|
37094
|
+
},
|
|
37095
|
+
// ---- Decision Rationale (v2.1.0) ----
|
|
37096
|
+
{
|
|
37097
|
+
name: "decision:propose",
|
|
37098
|
+
run: async () => {
|
|
37099
|
+
const r = await ctx.decisionManager.propose({
|
|
37100
|
+
context: "choosing storage",
|
|
37101
|
+
decision: "use SQLite",
|
|
37102
|
+
alternatives: ["JSONL"],
|
|
37103
|
+
consequences: ["ACID"]
|
|
37104
|
+
});
|
|
37105
|
+
if (!r.id) throw new Error("propose returned no id");
|
|
37106
|
+
}
|
|
37107
|
+
},
|
|
37108
|
+
{
|
|
37109
|
+
name: "decision:accept",
|
|
37110
|
+
run: async () => {
|
|
37111
|
+
const list = await ctx.decisionManager.list({ status: "proposed" });
|
|
37112
|
+
if (list.length === 0) throw new Error("no proposed decision to accept");
|
|
37113
|
+
const result = await ctx.decisionManager.accept(list[0].id);
|
|
37114
|
+
if (result !== "accepted") throw new Error(`accept returned ${result}`);
|
|
37115
|
+
}
|
|
37116
|
+
},
|
|
37117
|
+
{
|
|
37118
|
+
name: "decision:list",
|
|
37119
|
+
run: async () => {
|
|
37120
|
+
const list = await ctx.decisionManager.list({ status: "accepted" });
|
|
37121
|
+
if (list.length === 0) throw new Error("accepted decision missing from list");
|
|
37122
|
+
}
|
|
37123
|
+
},
|
|
37124
|
+
// ---- Heuristic Guidelines (v2.1.0) ----
|
|
37125
|
+
{
|
|
37126
|
+
name: "heuristic:add",
|
|
37127
|
+
run: async () => {
|
|
37128
|
+
const id = await ctx.heuristicManager.add({
|
|
37129
|
+
condition: "editing TypeScript",
|
|
37130
|
+
action: "run typecheck before commit"
|
|
37131
|
+
});
|
|
37132
|
+
if (!id) throw new Error("add_heuristic returned no id");
|
|
37133
|
+
}
|
|
37134
|
+
},
|
|
37135
|
+
{
|
|
37136
|
+
name: "heuristic:match",
|
|
37137
|
+
run: async () => {
|
|
37138
|
+
const matches2 = await ctx.heuristicManager.match("editing TypeScript file");
|
|
37139
|
+
if (matches2.length === 0) throw new Error("match returned no heuristics");
|
|
37140
|
+
}
|
|
37141
|
+
},
|
|
37142
|
+
// ---- Project Context (v2.1.0) ----
|
|
37143
|
+
{
|
|
37144
|
+
name: "project_context:upsert",
|
|
37145
|
+
run: async () => {
|
|
37146
|
+
const rec = await ctx.projectContextManager.upsert("smoke", {
|
|
37147
|
+
facts: ["smoke test project"]
|
|
37148
|
+
});
|
|
37149
|
+
if (!rec) throw new Error("upsert returned nothing");
|
|
37150
|
+
}
|
|
37151
|
+
},
|
|
37152
|
+
{
|
|
37153
|
+
name: "project_context:get",
|
|
37154
|
+
run: async () => {
|
|
37155
|
+
const rec = ctx.projectContextManager.get("smoke");
|
|
37156
|
+
if (!rec) throw new Error("project context not found after upsert");
|
|
37157
|
+
}
|
|
37158
|
+
},
|
|
37159
|
+
// ---- Tool Affordance (v2.1.0) ----
|
|
37160
|
+
{
|
|
37161
|
+
name: "tool_affordance:record + stats",
|
|
37162
|
+
run: async () => {
|
|
37163
|
+
await ctx.toolAffordanceManager.recordOutcome("test_tool", {
|
|
37164
|
+
outcome: "success",
|
|
37165
|
+
durationMs: 10
|
|
37166
|
+
});
|
|
37167
|
+
const stats = ctx.toolAffordanceManager.rollingStats("test_tool");
|
|
37168
|
+
if (!stats) throw new Error("rollingStats returned null after recording");
|
|
37169
|
+
}
|
|
37170
|
+
},
|
|
37171
|
+
// ---- Exclusion (do_not_remember) (v2.1.0) ----
|
|
37172
|
+
{
|
|
37173
|
+
name: "exclusion:add + check",
|
|
37174
|
+
run: async () => {
|
|
37175
|
+
await ctx.exclusionManager.add({ pattern: "secret-token" });
|
|
37176
|
+
const verdict = await ctx.exclusionManager.check("this contains secret-token");
|
|
37177
|
+
if (!verdict.blocked) throw new Error("exclusion check failed to block matching content");
|
|
37178
|
+
}
|
|
37179
|
+
},
|
|
37180
|
+
// ---- Observation Dedup (v2.1.0) ----
|
|
37181
|
+
{
|
|
37182
|
+
name: "observation_dedup:find",
|
|
37183
|
+
run: async () => {
|
|
37184
|
+
await ctx.entityManager.createEntities([
|
|
37185
|
+
{ name: "DupA", entityType: "note", observations: ["shared dedup fact"] },
|
|
37186
|
+
{ name: "DupB", entityType: "note", observations: ["shared dedup fact"] }
|
|
37187
|
+
]);
|
|
37188
|
+
const groups = await ctx.observationDedupManager.findDuplicateObservations({});
|
|
37189
|
+
if (groups.length === 0) throw new Error("dedup found 0 groups despite seeded duplicates");
|
|
37190
|
+
}
|
|
37191
|
+
},
|
|
37192
|
+
// ---- Spell Correction (v2.1.0) ----
|
|
37193
|
+
{
|
|
37194
|
+
name: "spell:rebuild + suggest",
|
|
37195
|
+
run: async () => {
|
|
37196
|
+
await ctx.spellChecker.rebuild();
|
|
37197
|
+
const suggestions = await ctx.spellChecker.suggest("Alpa", { limit: 3 });
|
|
37198
|
+
if (!Array.isArray(suggestions)) throw new Error("suggest did not return an array");
|
|
37199
|
+
}
|
|
37200
|
+
}
|
|
37201
|
+
];
|
|
37202
|
+
}
|
|
37203
|
+
function printSummary(results, totalMs, verbose) {
|
|
37204
|
+
const passed = results.filter((r) => r.ok).length;
|
|
37205
|
+
const failed = results.length - passed;
|
|
37206
|
+
if (!verbose) {
|
|
37207
|
+
for (const r of results) {
|
|
37208
|
+
const marker = r.ok ? formatSuccess("\u2713") : formatError("\u2717");
|
|
37209
|
+
const tail = r.ok ? "" : ` \u2014 ${r.error}`;
|
|
37210
|
+
console.log(` ${marker} ${r.name}${tail}`);
|
|
37211
|
+
}
|
|
37212
|
+
}
|
|
37213
|
+
console.log("");
|
|
37214
|
+
if (failed === 0) {
|
|
37215
|
+
console.log(formatSuccess(`Smoke test passed: ${passed}/${results.length} steps in ${totalMs.toFixed(0)} ms`));
|
|
37216
|
+
} else {
|
|
37217
|
+
console.log(formatError(`Smoke test FAILED: ${failed} failing / ${results.length} total in ${totalMs.toFixed(0)} ms`));
|
|
37218
|
+
}
|
|
37219
|
+
}
|
|
37220
|
+
function registerSmokeCommand(program2) {
|
|
37221
|
+
program2.command("smoke").description("Run a per-category end-to-end smoke test (~30 ops) against a fresh temp graph").option("-s, --storage <path>", "Storage path for the smoke run (default: temp dir)").option("-k, --keep", "Preserve the smoke graph after the run and print its path (default: cleanup)").option("-v, --verbose", "Print each step as it runs (default: print summary only)").action(async (opts) => {
|
|
37222
|
+
const verbose = Boolean(opts.verbose);
|
|
37223
|
+
const keep = Boolean(opts.keep);
|
|
37224
|
+
const storagePath = opts.storage ? opts.storage : await fs17.mkdtemp(join8(tmpdir(), "memoryjs-smoke-"));
|
|
37225
|
+
const graphPath = opts.storage ? storagePath : join8(storagePath, "graph.jsonl");
|
|
37226
|
+
if (verbose) console.log(`Smoke storage: ${graphPath}`);
|
|
37227
|
+
const ctx = new ManagerContext(graphPath);
|
|
37228
|
+
const steps = buildSteps(ctx);
|
|
37229
|
+
const start = performance.now();
|
|
37230
|
+
const results = await runSteps(steps, verbose);
|
|
37231
|
+
const totalMs = performance.now() - start;
|
|
37232
|
+
printSummary(results, totalMs, verbose);
|
|
37233
|
+
if (keep) {
|
|
37234
|
+
console.log(`
|
|
37235
|
+
Keeping smoke graph at: ${graphPath}`);
|
|
37236
|
+
} else if (!opts.storage) {
|
|
37237
|
+
try {
|
|
37238
|
+
await fs17.rm(storagePath, { recursive: true, force: true });
|
|
37239
|
+
} catch {
|
|
37240
|
+
}
|
|
37241
|
+
}
|
|
37242
|
+
const failed = results.filter((r) => !r.ok).length;
|
|
37243
|
+
if (failed > 0) process.exit(1);
|
|
37244
|
+
});
|
|
37245
|
+
}
|
|
37246
|
+
|
|
36895
37247
|
// src/cli/commands/index.ts
|
|
36896
37248
|
function registerCommands(program2) {
|
|
36897
37249
|
registerEntityCommands(program2);
|
|
@@ -36907,6 +37259,7 @@ function registerCommands(program2) {
|
|
|
36907
37259
|
registerDecisionCommands(program2);
|
|
36908
37260
|
registerProjectContextCommands(program2);
|
|
36909
37261
|
registerToolAffordanceCommands(program2);
|
|
37262
|
+
registerSmokeCommand(program2);
|
|
36910
37263
|
}
|
|
36911
37264
|
|
|
36912
37265
|
// src/cli/index.ts
|
|
@@ -36914,7 +37267,7 @@ init_logger();
|
|
|
36914
37267
|
import { readFileSync as readFileSync4 } from "fs";
|
|
36915
37268
|
import { createInterface as createInterface2 } from "readline";
|
|
36916
37269
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
36917
|
-
import { dirname as dirname9, join as
|
|
37270
|
+
import { dirname as dirname9, join as join9 } from "path";
|
|
36918
37271
|
process.on("unhandledRejection", (reason) => {
|
|
36919
37272
|
logger.error("Unhandled promise rejection:", reason);
|
|
36920
37273
|
});
|
|
@@ -36925,7 +37278,7 @@ function getVersion() {
|
|
|
36925
37278
|
try {
|
|
36926
37279
|
const __filename2 = fileURLToPath3(import.meta.url);
|
|
36927
37280
|
const __dirname2 = dirname9(__filename2);
|
|
36928
|
-
const pkgPath =
|
|
37281
|
+
const pkgPath = join9(__dirname2, "..", "..", "package.json");
|
|
36929
37282
|
const pkg = JSON.parse(readFileSync4(pkgPath, "utf-8"));
|
|
36930
37283
|
return pkg.version;
|
|
36931
37284
|
} catch {
|