@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
- writeFileSync(marker, "");
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
+ };
@@ -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
- Hooks inject engrams automatically on every first message \u2014 you do not need to call \`plur_session_start\` manually (though you can for explicit session tracking).
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. **Automatic**: Hooks inject relevant engrams on first message \u2014 no action needed
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 (9): ${hooksStatus}`);
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.2";
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.2",
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.2"
13
+ "@plur-ai/core": "0.8.3"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@types/node": "^25.5.0"