@prom.codes/saver 0.1.3 → 0.1.5

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.
Files changed (3) hide show
  1. package/README.md +19 -17
  2. package/dist/bin.js +67 -5
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,30 +9,32 @@ caveats and destructive sequences **verbatim**.
9
9
  It is the third prom.codes tool, alongside **prom.codes Context** (code retrieval)
10
10
  and **prom.codes Memory** (agent memory).
11
11
 
12
- ## Quick start
13
-
14
- ```jsonc
15
- // Claude Desktop / Cursor MCP config
16
- {
17
- "mcpServers": {
18
- "saver": {
19
- "command": "npx",
20
- "args": ["-y", "@prom.codes/saver@latest"],
21
- "env": { "PROMETHEUS_WORKSPACE_ROOT": "/absolute/path/to/your/repo" }
22
- }
23
- }
24
- }
12
+ ## Quick start (Claude Code)
13
+
14
+ ```bash
15
+ claude mcp add saver -- npx -y @prom.codes/saver@latest
25
16
  ```
26
17
 
18
+ No key needed. `claude mcp add` defaults to local scope; add `--scope project`
19
+ for a committable `.mcp.json`. Other hosts (Cursor, VS Code) use the same
20
+ command/args in their own config.
21
+
27
22
  Then ask your agent to run `setup` once per workspace. Levels: `lite` (filler
28
23
  only), `balanced` (default — lean, not terse), `aggressive` (telegraphic, opt-in,
29
24
  never for code/destructive work). The rules keep code, caveats, destructive steps
30
25
  and anything the agent needs later **verbatim** — it shapes phrasing, never what the
31
26
  agent does or remembers.
32
27
 
28
+ A second tool, `status`, reports where it would write, the key state, and which
29
+ runtimes already have the block installed. If a fresh window opens with no
30
+ project (workspace falls back to home), `setup` **refuses to write rule files
31
+ into your home dir** — open your project folder first.
32
+
33
33
  No database and no native modules — `npx` works turnkey everywhere (Node ≥ 20.10).
34
- An API key is **optional**: Saver works fully without one, and accepts the same
35
- `PROMETHEUS_API_KEY` (`prom_live_…`) as prom.codes Context and Memory to tie the
36
- install to your account.
34
+ The workspace is auto-detected (Claude Code `CLAUDE_PROJECT_DIR`); set
35
+ `PROMETHEUS_WORKSPACE_ROOT` only to target a different folder. An API key is
36
+ **optional**: the Saver works fully without one, and accepts the same
37
+ `PROMETHEUS_API_KEY` (`prom_live_<tag>_<secret>`) as prom.codes Context and
38
+ Memory to tie the install to your account.
37
39
 
38
- Docs: https://prom.codes/docs
40
+ Docs: https://prom.codes/docs/mcp/saver
package/dist/bin.js CHANGED
@@ -1,7 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // dist/bin.js
4
- import { resolve } from "node:path";
5
4
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
5
 
7
6
  // dist/key.js
@@ -14,13 +13,36 @@ function classifyKey(raw) {
14
13
  return KEY_PATTERN.test(key) ? "valid" : "malformed";
15
14
  }
16
15
 
16
+ // dist/workspace.js
17
+ import { homedir } from "node:os";
18
+ import { dirname, resolve } from "node:path";
19
+ function isHomeOrFilesystemRoot(root) {
20
+ const abs = resolve(root);
21
+ if (abs === "")
22
+ return true;
23
+ if (abs === resolve(homedir()))
24
+ return true;
25
+ if (dirname(abs) === abs)
26
+ return true;
27
+ return false;
28
+ }
29
+ function resolveWorkspaceRoot(env = process.env) {
30
+ const explicit = (env.PROMETHEUS_WORKSPACE_ROOT ?? "").trim();
31
+ if (explicit !== "")
32
+ return resolve(explicit);
33
+ const claude = (env.CLAUDE_PROJECT_DIR ?? "").trim();
34
+ if (claude !== "")
35
+ return resolve(claude);
36
+ return resolve(process.cwd());
37
+ }
38
+
17
39
  // dist/tools.js
18
40
  import { z } from "zod";
19
41
 
20
42
  // dist/setup.js
21
- import { existsSync } from "node:fs";
43
+ import { existsSync, readFileSync } from "node:fs";
22
44
  import { mkdir, readFile, writeFile } from "node:fs/promises";
23
- import { dirname, join } from "node:path";
45
+ import { dirname as dirname2, join } from "node:path";
24
46
  var SAVER_LEVELS = ["lite", "balanced", "aggressive"];
25
47
  var DEFAULT_LEVEL = "balanced";
26
48
  var SAVER_RUNTIMES = [
@@ -83,6 +105,18 @@ function detectRuntimes(workspaceRoot) {
83
105
  const found = SAVER_RUNTIMES.filter((rt) => existsSync(join(workspaceRoot, TARGETS[rt].detect)));
84
106
  return found.length > 0 ? found : ["agents"];
85
107
  }
108
+ function installedRuntimes(workspaceRoot) {
109
+ return SAVER_RUNTIMES.filter((rt) => {
110
+ const p = join(workspaceRoot, TARGETS[rt].relPath);
111
+ if (!existsSync(p))
112
+ return false;
113
+ try {
114
+ return readFileSync(p, "utf-8").includes(BLOCK_START);
115
+ } catch {
116
+ return false;
117
+ }
118
+ });
119
+ }
86
120
  function upsertBlock(existing, block) {
87
121
  const marked = withMarkers(block);
88
122
  const start = existing.indexOf(BLOCK_START);
@@ -103,7 +137,7 @@ async function installRuntime(workspaceRoot, runtime, level) {
103
137
  if (exists && before === after) {
104
138
  return { runtime, path: absPath, action: "unchanged" };
105
139
  }
106
- await mkdir(dirname(absPath), { recursive: true });
140
+ await mkdir(dirname2(absPath), { recursive: true });
107
141
  await writeFile(absPath, after, "utf-8");
108
142
  return { runtime, path: absPath, action: exists ? "updated" : "created" };
109
143
  }
@@ -119,14 +153,24 @@ var setupInput = {
119
153
  level: levelEnum.optional(),
120
154
  runtimes: z.array(runtimeEnum).min(1).optional()
121
155
  };
156
+ var emptyInput = {};
122
157
  function registerTools(server, deps) {
123
158
  const { workspaceRoot, apiKey, requireKey } = deps;
124
159
  const keyState = deps.keyState ?? (apiKey !== void 0 ? "valid" : "absent");
160
+ const rootIsHomeOrFsRoot = isHomeOrFilesystemRoot(workspaceRoot);
125
161
  server.registerTool("setup", {
126
162
  title: "Install the prom.codes Saver efficient-output rules",
127
163
  description: "Idempotently install the prom.codes Saver efficient-output rule block into this workspace's agent runtime configs (CLAUDE.md / .cursor/rules / .augment/rules / AGENTS.md). The rules cut token cost by trimming the agent's own prose (preamble, narration, pasted tool output) while keeping code, caveats and destructive sequences verbatim. `level`: `lite` (filler only) | `balanced` (default \u2014 lean, not terse) | `aggressive` (telegraphic, opt-in, never for code/destructive). Without `runtimes` it auto-detects which are present (fallback: agents). Re-running updates the block in place. Run this once per workspace.",
128
164
  inputSchema: setupInput
129
165
  }, async (args) => {
166
+ if (rootIsHomeOrFsRoot) {
167
+ return textResult({
168
+ ok: false,
169
+ installed: false,
170
+ workspaceRoot,
171
+ reason: "Workspace resolved to your home directory or a filesystem root \u2014 refusing to write rule files there. Open your project folder (Claude Code passes it via CLAUDE_PROJECT_DIR) or set PROMETHEUS_WORKSPACE_ROOT, then retry."
172
+ });
173
+ }
130
174
  if (requireKey === true && keyState !== "valid") {
131
175
  return {
132
176
  isError: true,
@@ -157,6 +201,24 @@ function registerTools(server, deps) {
157
201
  results
158
202
  });
159
203
  });
204
+ server.registerTool("status", {
205
+ title: "Saver status / health check",
206
+ description: "Health check for the prom.codes Saver in this workspace: the resolved workspace root, whether the API key is present/valid (optional \u2014 the Saver works keyless), which agent runtimes are detected here, and which of them already have the Saver rule block installed. Call this to confirm where it would write and whether `setup` has already run.",
207
+ inputSchema: emptyInput
208
+ }, async () => {
209
+ const detected = detectRuntimes(workspaceRoot);
210
+ const installed = rootIsHomeOrFsRoot ? [] : installedRuntimes(workspaceRoot);
211
+ const summary = rootIsHomeOrFsRoot ? `Workspace resolved to ${workspaceRoot} (home/root) \u2014 setup is refused here. Open your project folder.` : installed.length > 0 ? `Saver installed in ${workspaceRoot} for: ${installed.join(", ")}.` : `Saver not yet installed in ${workspaceRoot}. Run setup. Detected runtimes: ${detected.join(", ")}.`;
212
+ return textResult({
213
+ installed: installed.length > 0,
214
+ workspaceRoot,
215
+ rootIsHomeOrFsRoot,
216
+ key: { keyed: apiKey !== void 0, keyState, requireKey: requireKey === true },
217
+ detectedRuntimes: detected,
218
+ installedRuntimes: installed,
219
+ summary
220
+ });
221
+ });
160
222
  }
161
223
 
162
224
  // dist/server.js
@@ -180,7 +242,7 @@ function createServer(deps, options = {}) {
180
242
 
181
243
  // dist/bin.js
182
244
  async function main() {
183
- const workspaceRoot = resolve(process.env.PROMETHEUS_WORKSPACE_ROOT ?? process.cwd());
245
+ const workspaceRoot = resolveWorkspaceRoot(process.env);
184
246
  const rawKey = process.env[API_KEY_ENV];
185
247
  const keyState = classifyKey(rawKey);
186
248
  const requireKey = /^(1|true|yes|on)$/i.test(process.env[REQUIRE_KEY_ENV] ?? "");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prom.codes/saver",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "prom.codes Saver — cut token cost without cutting quality, as an MCP installer.",
5
5
  "type": "module",
6
6
  "bin": {