@plur-ai/cli 0.9.9 → 0.9.11

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.
@@ -0,0 +1,168 @@
1
+ import {
2
+ exit,
3
+ outputJson,
4
+ outputText,
5
+ shouldOutputJson
6
+ } from "./chunk-7U4W4J3G.js";
7
+ import {
8
+ createPlur
9
+ } from "./chunk-O6WTH7H7.js";
10
+
11
+ // src/commands/tensions.ts
12
+ function makeHttpLlm(baseUrl, apiKey, model = "gpt-4o-mini") {
13
+ return async (prompt) => {
14
+ const response = await fetch(`${baseUrl.replace(/\/$/, "")}/chat/completions`, {
15
+ method: "POST",
16
+ headers: {
17
+ "Content-Type": "application/json",
18
+ Authorization: `Bearer ${apiKey}`
19
+ },
20
+ body: JSON.stringify({
21
+ model,
22
+ messages: [{ role: "user", content: prompt }],
23
+ temperature: 0.1
24
+ })
25
+ });
26
+ if (!response.ok) {
27
+ throw new Error(`LLM API error: ${response.status} ${response.statusText}`);
28
+ }
29
+ const data = await response.json();
30
+ return data.choices?.[0]?.message?.content ?? "";
31
+ };
32
+ }
33
+ function getLlmFunction() {
34
+ const openrouterKey = process.env.OPENROUTER_API_KEY;
35
+ const openaiKey = process.env.OPENAI_API_KEY;
36
+ if (openrouterKey) return makeHttpLlm("https://openrouter.ai/api/v1", openrouterKey, "openai/gpt-4o-mini");
37
+ if (openaiKey) return makeHttpLlm("https://api.openai.com/v1", openaiKey, "gpt-4o-mini");
38
+ return void 0;
39
+ }
40
+ async function run(args, flags) {
41
+ let scan = false;
42
+ let scope;
43
+ let domain;
44
+ let minConfidence = 0.7;
45
+ let maxPairs = 50;
46
+ let llmBaseUrl;
47
+ let llmApiKey;
48
+ let llmModel;
49
+ let i = 0;
50
+ while (i < args.length) {
51
+ const arg = args[i];
52
+ if (arg === "--scan") {
53
+ scan = true;
54
+ i++;
55
+ } else if (arg === "--scope" && i + 1 < args.length) {
56
+ scope = args[++i];
57
+ i++;
58
+ } else if (arg === "--domain" && i + 1 < args.length) {
59
+ domain = args[++i];
60
+ i++;
61
+ } else if (arg === "--min-confidence" && i + 1 < args.length) {
62
+ minConfidence = parseFloat(args[++i]);
63
+ i++;
64
+ } else if (arg === "--max-pairs" && i + 1 < args.length) {
65
+ maxPairs = parseInt(args[++i], 10);
66
+ i++;
67
+ } else if (arg === "--llm-base-url" && i + 1 < args.length) {
68
+ llmBaseUrl = args[++i];
69
+ i++;
70
+ } else if (arg === "--llm-api-key" && i + 1 < args.length) {
71
+ llmApiKey = args[++i];
72
+ i++;
73
+ } else if (arg === "--model" && i + 1 < args.length) {
74
+ llmModel = args[++i];
75
+ i++;
76
+ } else {
77
+ i++;
78
+ }
79
+ }
80
+ const plur = createPlur(flags, { readonly: true });
81
+ const engrams = plur.list({ scope, domain });
82
+ if (scan) {
83
+ const llm = llmBaseUrl ? makeHttpLlm(llmBaseUrl, llmApiKey ?? "", llmModel) : getLlmFunction();
84
+ if (!llm) {
85
+ exit(
86
+ 1,
87
+ "tensions --scan requires an LLM.\nSet OPENROUTER_API_KEY or OPENAI_API_KEY, or pass --llm-base-url + --llm-api-key."
88
+ );
89
+ return;
90
+ }
91
+ if (!shouldOutputJson(flags)) {
92
+ outputText(`Scanning ${engrams.length} engrams for contradictions\u2026`);
93
+ if (scope) outputText(` scope: ${scope}`);
94
+ if (domain) outputText(` domain: ${domain}`);
95
+ outputText(` min-confidence: ${minConfidence} max-pairs: ${maxPairs}`);
96
+ outputText("");
97
+ }
98
+ const { scanForTensions } = await import("@plur-ai/core");
99
+ const result = await scanForTensions(engrams, llm, { min_confidence: minConfidence, max_pairs: maxPairs });
100
+ if (shouldOutputJson(flags)) {
101
+ outputJson({
102
+ pairs_checked: result.pairs_checked,
103
+ count: result.new_tensions,
104
+ tensions: result.tensions.map((t) => ({
105
+ engram_a: { id: t.id_a, statement: t.statement_a },
106
+ engram_b: { id: t.id_b, statement: t.statement_b },
107
+ confidence: t.confidence,
108
+ reason: t.reason
109
+ }))
110
+ });
111
+ return;
112
+ }
113
+ outputText(`Checked: ${result.pairs_checked} candidate pairs`);
114
+ outputText(`Found: ${result.new_tensions} tension${result.new_tensions === 1 ? "" : "s"} (confidence >= ${minConfidence})`);
115
+ outputText("");
116
+ if (result.tensions.length === 0) {
117
+ outputText("No contradictions detected.");
118
+ return;
119
+ }
120
+ for (const t of result.tensions) {
121
+ outputText(`\u2500\u2500 TENSION (confidence: ${t.confidence.toFixed(2)}) \u2500\u2500`);
122
+ outputText(` A [${t.id_a}]: ${t.statement_a}`);
123
+ outputText(` B [${t.id_b}]: ${t.statement_b}`);
124
+ outputText(` Reason: ${t.reason}`);
125
+ outputText("");
126
+ }
127
+ outputText("Next steps:");
128
+ outputText(" Resolve: determine which statement is correct, retire the other via plur forget <id>");
129
+ outputText(" Dismiss: if not a real conflict, both statements can coexist");
130
+ return;
131
+ }
132
+ const tensions = [];
133
+ const seen = /* @__PURE__ */ new Set();
134
+ for (const engram of engrams) {
135
+ if (!engram.relations?.conflicts?.length) continue;
136
+ for (const conflictId of engram.relations.conflicts) {
137
+ const pairKey = [engram.id, conflictId].sort().join(":");
138
+ if (seen.has(pairKey)) continue;
139
+ seen.add(pairKey);
140
+ const other = engrams.find((e) => e.id === conflictId);
141
+ if (!other) continue;
142
+ tensions.push({
143
+ engram_a: { id: engram.id, statement: engram.statement },
144
+ engram_b: { id: other.id, statement: other.statement },
145
+ detected_at: engram.activation.last_accessed
146
+ });
147
+ }
148
+ }
149
+ if (shouldOutputJson(flags)) {
150
+ outputJson({ tensions, count: tensions.length });
151
+ return;
152
+ }
153
+ if (tensions.length === 0) {
154
+ outputText("No stored tensions. Run `plur tensions --scan` to detect live contradictions.");
155
+ return;
156
+ }
157
+ outputText(`Stored tensions: ${tensions.length}`);
158
+ outputText("");
159
+ for (const t of tensions) {
160
+ outputText(` A [${t.engram_a.id}]: ${t.engram_a.statement}`);
161
+ outputText(` B [${t.engram_b.id}]: ${t.engram_b.statement}`);
162
+ outputText(` Detected: ${t.detected_at}`);
163
+ outputText("");
164
+ }
165
+ }
166
+ export {
167
+ run
168
+ };
package/dist/index.js CHANGED
@@ -49,7 +49,7 @@ function createPlur(flags2) {
49
49
  }
50
50
 
51
51
  // src/index.ts
52
- var VERSION = "0.9.9";
52
+ var VERSION = "0.9.11";
53
53
  var argv = process.argv.slice(2);
54
54
  if (argv.includes("--version") || argv.includes("-v")) {
55
55
  console.log(VERSION);
@@ -82,7 +82,9 @@ Commands:
82
82
  stores list List configured stores
83
83
  stores add <path> Add a knowledge store
84
84
  init Install Claude Code hooks + register plur MCP server
85
+ init-remote Opt this project into recall from a PLUR Enterprise server
85
86
  doctor Diagnose Claude Code / Claude Desktop integration
87
+ tensions [--scan] List or scan for engram contradictions
86
88
  audit [--source X] Audit working memory (claude-code|claw|hermes) for conflicts vs engrams
87
89
  hook-inject (internal) Hook handler for engram injection
88
90
  hook-observe (internal) Hook handler for observation capture
@@ -124,7 +126,9 @@ var COMMANDS = {
124
126
  stores: "./commands/stores.js",
125
127
  migrate: "./commands/migrate.js",
126
128
  init: "./commands/init.js",
129
+ "init-remote": "./commands/init-remote.js",
127
130
  doctor: "./commands/doctor.js",
131
+ tensions: "./commands/tensions.js",
128
132
  audit: "./commands/audit.js",
129
133
  "hook-inject": "./commands/hook-inject.js",
130
134
  "hook-observe": "./commands/hook-observe.js",
@@ -133,7 +137,11 @@ var COMMANDS = {
133
137
  "hook-session-mark": "./commands/hook-session-mark.js",
134
138
  "hook-session-remind": "./commands/hook-session-remind.js",
135
139
  "hook-correction-detect": "./commands/hook-correction-detect.js",
136
- "hook-revert-detect": "./commands/hook-revert-detect.js"
140
+ "hook-revert-detect": "./commands/hook-revert-detect.js",
141
+ // Hidden internal subcommand — spawned by `plur doctor` to isolate the
142
+ // ONNX embedder probe (issue #197). If the probe crashes with SIGABRT
143
+ // on libc++ thread pool cleanup, only the subprocess dies; doctor stays alive.
144
+ "_embedder-probe": "./commands/embedder-probe.js"
137
145
  };
138
146
  if (!command || !COMMANDS[command]) {
139
147
  exit(1, `Unknown command: ${command}. Run 'plur --help' for usage.`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plur-ai/cli",
3
- "version": "0.9.9",
3
+ "version": "0.9.11",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "plur": "dist/index.js"
@@ -10,7 +10,7 @@
10
10
  "dist"
11
11
  ],
12
12
  "dependencies": {
13
- "@plur-ai/core": "0.9.9"
13
+ "@plur-ai/core": "0.9.11"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^25.5.0"