@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.
- package/README.md +19 -17
- package/dist/bin.js +67 -5
- 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
|
-
```
|
|
15
|
-
|
|
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
|
-
|
|
35
|
-
`
|
|
36
|
-
|
|
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(
|
|
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 =
|
|
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] ?? "");
|