@plur-ai/cli 0.8.2 → 0.8.4
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.
|
@@ -167,8 +167,7 @@ ${parts2.join("\n")}` };
|
|
|
167
167
|
} else {
|
|
168
168
|
task = input.prompt || "";
|
|
169
169
|
if (!task) {
|
|
170
|
-
|
|
171
|
-
return;
|
|
170
|
+
task = "general session";
|
|
172
171
|
}
|
|
173
172
|
const sessionId2 = randomUUID();
|
|
174
173
|
writeFileSync(marker, JSON.stringify({ task, sessionId: sessionId2 }));
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
// src/commands/hook-session-guard.ts
|
|
2
|
+
import { readSync, existsSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
var EXEMPT_TOOLS = /* @__PURE__ */ new Set([
|
|
6
|
+
"mcp__plur__plur_session_start",
|
|
7
|
+
"ToolSearch"
|
|
8
|
+
]);
|
|
9
|
+
function readStdinRaw() {
|
|
10
|
+
try {
|
|
11
|
+
const chunks = [];
|
|
12
|
+
const buf = Buffer.alloc(65536);
|
|
13
|
+
while (true) {
|
|
14
|
+
try {
|
|
15
|
+
const n = readSync(0, buf, 0, buf.length, null);
|
|
16
|
+
if (n === 0) break;
|
|
17
|
+
chunks.push(Buffer.from(buf.subarray(0, n)));
|
|
18
|
+
} catch {
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
23
|
+
} catch {
|
|
24
|
+
return "";
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
function sentinelPath(sessionId) {
|
|
28
|
+
return join(tmpdir(), `plur-session-${sessionId}`);
|
|
29
|
+
}
|
|
30
|
+
async function run(_args, _flags) {
|
|
31
|
+
const raw = readStdinRaw();
|
|
32
|
+
let data;
|
|
33
|
+
try {
|
|
34
|
+
data = JSON.parse(raw);
|
|
35
|
+
} catch {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const toolName = data.tool_name ?? "";
|
|
39
|
+
const sessionId = data.session_id ?? "";
|
|
40
|
+
if (EXEMPT_TOOLS.has(toolName)) return;
|
|
41
|
+
if (!sessionId) return;
|
|
42
|
+
if (existsSync(sentinelPath(sessionId))) return;
|
|
43
|
+
const output = {
|
|
44
|
+
hookSpecificOutput: {
|
|
45
|
+
hookEventName: "PreToolUse",
|
|
46
|
+
permissionDecision: "deny",
|
|
47
|
+
permissionDecisionReason: "BLOCKED: plur_session_start has not been called yet. You MUST call mcp__plur__plur_session_start before using any other tool. Use ToolSearch to load it first if needed."
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
process.stdout.write(JSON.stringify(output));
|
|
51
|
+
}
|
|
52
|
+
export {
|
|
53
|
+
run
|
|
54
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/commands/hook-session-mark.ts
|
|
2
|
+
import { readSync, writeFileSync } from "fs";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { tmpdir } from "os";
|
|
5
|
+
function readStdinRaw() {
|
|
6
|
+
try {
|
|
7
|
+
const chunks = [];
|
|
8
|
+
const buf = Buffer.alloc(65536);
|
|
9
|
+
while (true) {
|
|
10
|
+
try {
|
|
11
|
+
const n = readSync(0, buf, 0, buf.length, null);
|
|
12
|
+
if (n === 0) break;
|
|
13
|
+
chunks.push(Buffer.from(buf.subarray(0, n)));
|
|
14
|
+
} catch {
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
19
|
+
} catch {
|
|
20
|
+
return "";
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
async function run(_args, _flags) {
|
|
24
|
+
const raw = readStdinRaw();
|
|
25
|
+
let data;
|
|
26
|
+
try {
|
|
27
|
+
data = JSON.parse(raw);
|
|
28
|
+
} catch {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
const sessionId = data.session_id ?? "";
|
|
32
|
+
if (!sessionId) return;
|
|
33
|
+
const sentinel = join(tmpdir(), `plur-session-${sessionId}`);
|
|
34
|
+
try {
|
|
35
|
+
writeFileSync(sentinel, "");
|
|
36
|
+
} catch {
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export {
|
|
40
|
+
run
|
|
41
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// src/commands/hook-session-remind.ts
|
|
2
|
+
async function run(_args, _flags) {
|
|
3
|
+
const output = {
|
|
4
|
+
hookSpecificOutput: {
|
|
5
|
+
hookEventName: "SessionStart",
|
|
6
|
+
additionalContext: "MANDATORY FIRST ACTION \u2014 DO THIS NOW BEFORE ANYTHING ELSE:\n1. Call ToolSearch with query 'select:mcp__plur__plur_session_start'\n2. Call mcp__plur__plur_session_start with the user's task description\n3. Only THEN proceed with any other work\n\nA PreToolUse guard will BLOCK all other tool calls until this is done. This is not optional. This is not a suggestion. Do it now."
|
|
7
|
+
}
|
|
8
|
+
};
|
|
9
|
+
process.stdout.write(JSON.stringify(output));
|
|
10
|
+
}
|
|
11
|
+
export {
|
|
12
|
+
run
|
|
13
|
+
};
|
package/dist/commands/init.js
CHANGED
|
@@ -16,6 +16,19 @@ import { join } from "path";
|
|
|
16
16
|
import { homedir } from "os";
|
|
17
17
|
var CLI = "npx @plur-ai/cli";
|
|
18
18
|
var PLUR_HOOKS = {
|
|
19
|
+
// --- Session enforcement ---
|
|
20
|
+
// These three hooks ensure plur_session_start is always called first.
|
|
21
|
+
// Without session start, feedback loops, episode tracking, and scoped
|
|
22
|
+
// injection don't work. The guard blocks all tools until the sentinel
|
|
23
|
+
// file exists; the mark creates it after plur_session_start succeeds.
|
|
24
|
+
// Forceful directive at session open
|
|
25
|
+
SessionStart: [
|
|
26
|
+
{
|
|
27
|
+
hooks: [
|
|
28
|
+
{ type: "command", command: `${CLI} hook-session-remind`, timeout: 3 }
|
|
29
|
+
]
|
|
30
|
+
}
|
|
31
|
+
],
|
|
19
32
|
// --- Session lifecycle ---
|
|
20
33
|
// First message: inject engrams based on the prompt.
|
|
21
34
|
// Subsequent messages: periodic reminder to call plur_learn (~1ms skip).
|
|
@@ -35,8 +48,16 @@ var PLUR_HOOKS = {
|
|
|
35
48
|
]
|
|
36
49
|
}
|
|
37
50
|
],
|
|
38
|
-
// --- Contextual injection ---
|
|
51
|
+
// --- Contextual injection + session guard ---
|
|
39
52
|
PreToolUse: [
|
|
53
|
+
// Session guard — blocks all tools until plur_session_start is called.
|
|
54
|
+
// Must be first so it runs before any other PreToolUse hook.
|
|
55
|
+
{
|
|
56
|
+
matcher: "*",
|
|
57
|
+
hooks: [
|
|
58
|
+
{ type: "command", command: `${CLI} hook-session-guard`, timeout: 3 }
|
|
59
|
+
]
|
|
60
|
+
},
|
|
40
61
|
// Full injection when entering plan mode — planning needs broad context
|
|
41
62
|
{
|
|
42
63
|
matcher: "EnterPlanMode",
|
|
@@ -66,8 +87,15 @@ var PLUR_HOOKS = {
|
|
|
66
87
|
]
|
|
67
88
|
}
|
|
68
89
|
],
|
|
69
|
-
// Observation capture — log tool results
|
|
90
|
+
// Observation capture — log tool results + session sentinel
|
|
70
91
|
PostToolUse: [
|
|
92
|
+
// Session sentinel — creates marker file after plur_session_start succeeds
|
|
93
|
+
{
|
|
94
|
+
matcher: "mcp__plur__plur_session_start",
|
|
95
|
+
hooks: [
|
|
96
|
+
{ type: "command", command: `${CLI} hook-session-mark`, timeout: 3 }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
71
99
|
{
|
|
72
100
|
matcher: "Bash|Edit|Write|Agent",
|
|
73
101
|
hooks: [
|
|
@@ -103,11 +131,11 @@ You have persistent memory via PLUR. Corrections, preferences, and conventions p
|
|
|
103
131
|
|
|
104
132
|
PLUR is installed **globally** \u2014 one MCP server, one engram store (\`~/.plur/\`), available in every project. You do NOT need per-project installation. The \`plur\` MCP server provides tools named \`plur_session_start\`, \`plur_learn\`, \`plur_recall_hybrid\`, \`plur_feedback\`, \`plur_session_end\`, etc. If you cannot find these tools, run \`plur doctor\` to diagnose. Do **not** substitute tools from other MCP servers (e.g. \`datacore_*\`) \u2014 those belong to a different system.
|
|
105
133
|
|
|
106
|
-
|
|
134
|
+
A PreToolUse guard enforces that \`plur_session_start\` is called at the beginning of every session. All other tools are blocked until this is done. The flow is: ToolSearch to load \`plur_session_start\` \u2192 call it with a task description \u2192 proceed.
|
|
107
135
|
|
|
108
136
|
### Session Workflow
|
|
109
137
|
|
|
110
|
-
1. **
|
|
138
|
+
1. **Start**: Call \`plur_session_start\` with task description \u2014 enforced by guard hook
|
|
111
139
|
2. **Learn**: When corrected or discovering something new, call \`plur_learn\` immediately
|
|
112
140
|
3. **Recall**: Before answering factual questions, call \`plur_recall_hybrid\` \u2014 check memory first
|
|
113
141
|
4. **Feedback**: Rate injected engrams with \`plur_feedback\` (positive/negative) \u2014 trains relevance
|
|
@@ -261,7 +289,10 @@ async function run(args, flags) {
|
|
|
261
289
|
outputText(`MCP server (plur): ${mcpStatus}`);
|
|
262
290
|
outputText(` command: ${entry.command} ${entry.args.join(" ")}`);
|
|
263
291
|
outputText("");
|
|
264
|
-
outputText(`Hooks (
|
|
292
|
+
outputText(`Hooks (12): ${hooksStatus}`);
|
|
293
|
+
outputText(" SessionStart \u2014 enforce plur_session_start before any work");
|
|
294
|
+
outputText(" PreToolUse \u2014 session guard (blocks tools until session started)");
|
|
295
|
+
outputText(" PostToolUse \u2014 session sentinel (marks session as started)");
|
|
265
296
|
outputText(" UserPromptSubmit \u2014 inject engrams + auto-start session");
|
|
266
297
|
outputText(" PostCompact \u2014 re-inject engrams after context compaction");
|
|
267
298
|
outputText(" PreToolUse \u2014 contextual injection (plan mode, skills, agents)");
|
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.8.
|
|
52
|
+
var VERSION = "0.8.4";
|
|
53
53
|
var argv = process.argv.slice(2);
|
|
54
54
|
if (argv.includes("--version") || argv.includes("-v")) {
|
|
55
55
|
console.log(VERSION);
|
|
@@ -84,6 +84,9 @@ Commands:
|
|
|
84
84
|
hook-inject (internal) Hook handler for engram injection
|
|
85
85
|
hook-observe (internal) Hook handler for observation capture
|
|
86
86
|
hook-learn-check (internal) Hook handler for learning reflection
|
|
87
|
+
hook-session-guard (internal) Hook handler for session enforcement
|
|
88
|
+
hook-session-mark (internal) Hook handler for session sentinel
|
|
89
|
+
hook-session-remind (internal) Hook handler for session start reminder
|
|
87
90
|
|
|
88
91
|
Global flags:
|
|
89
92
|
--json Force JSON output (auto-detected when piped)
|
|
@@ -117,7 +120,10 @@ var COMMANDS = {
|
|
|
117
120
|
doctor: "./commands/doctor.js",
|
|
118
121
|
"hook-inject": "./commands/hook-inject.js",
|
|
119
122
|
"hook-observe": "./commands/hook-observe.js",
|
|
120
|
-
"hook-learn-check": "./commands/hook-learn-check.js"
|
|
123
|
+
"hook-learn-check": "./commands/hook-learn-check.js",
|
|
124
|
+
"hook-session-guard": "./commands/hook-session-guard.js",
|
|
125
|
+
"hook-session-mark": "./commands/hook-session-mark.js",
|
|
126
|
+
"hook-session-remind": "./commands/hook-session-remind.js"
|
|
121
127
|
};
|
|
122
128
|
if (!command || !COMMANDS[command]) {
|
|
123
129
|
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.8.
|
|
3
|
+
"version": "0.8.4",
|
|
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.8.
|
|
13
|
+
"@plur-ai/core": "0.8.3"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
|
16
16
|
"@types/node": "^25.5.0"
|