@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.
- package/README.md +3 -2
- package/dist/commands/doctor.js +131 -28
- package/dist/commands/embedder-probe.js +47 -0
- package/dist/commands/hook-inject.js +178 -31
- package/dist/commands/hook-session-guard.js +26 -1
- package/dist/commands/init-remote.js +298 -0
- package/dist/commands/init.js +155 -109
- package/dist/commands/stores.js +1 -1
- package/dist/commands/tensions.js +168 -0
- package/dist/index.js +10 -2
- package/package.json +2 -2
|
@@ -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.
|
|
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.
|
|
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.
|
|
13
|
+
"@plur-ai/core": "0.9.11"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^25.5.0"
|