memento-mori-jester 0.1.3
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/LICENSE +21 -0
- package/README.md +313 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +789 -0
- package/dist/cli.js.map +1 -0
- package/dist/config.d.ts +33 -0
- package/dist/config.js +295 -0
- package/dist/config.js.map +1 -0
- package/dist/core.d.ts +7 -0
- package/dist/core.js +489 -0
- package/dist/core.js.map +1 -0
- package/dist/format.d.ts +2 -0
- package/dist/format.js +25 -0
- package/dist/format.js.map +1 -0
- package/dist/hooks.d.ts +26 -0
- package/dist/hooks.js +133 -0
- package/dist/hooks.js.map +1 -0
- package/dist/server.d.ts +2 -0
- package/dist/server.js +87 -0
- package/dist/server.js.map +1 -0
- package/dist/types.d.ts +61 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/docs/AGENTS.md +145 -0
- package/docs/GITHUB_ACTIONS.md +116 -0
- package/docs/RELEASE.md +119 -0
- package/examples/github-action.yml +21 -0
- package/examples/jester.config.json +28 -0
- package/package.json +71 -0
- package/scripts/install.ps1 +30 -0
- package/scripts/install.sh +30 -0
- package/scripts/run-tests.mjs +32 -0
package/dist/hooks.js
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { chmod, mkdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import { dirname, resolve } from "node:path";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
export const hookNames = ["pre-commit", "pre-push"];
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
const marker = "memento-mori-jester managed hook";
|
|
8
|
+
export async function installHook(options) {
|
|
9
|
+
const cwd = options.cwd ?? process.cwd();
|
|
10
|
+
const path = await gitHookPath(options.hook, cwd);
|
|
11
|
+
const existing = await readOptional(path);
|
|
12
|
+
if (existing && !existing.includes(marker) && !options.force) {
|
|
13
|
+
throw new Error(`Refusing to overwrite existing ${options.hook} hook at ${path}. Re-run with --force to replace it.`);
|
|
14
|
+
}
|
|
15
|
+
await mkdir(dirname(path), { recursive: true });
|
|
16
|
+
await writeFile(path, renderHook(options), "utf8");
|
|
17
|
+
await chmod(path, 0o755);
|
|
18
|
+
return {
|
|
19
|
+
hook: options.hook,
|
|
20
|
+
path,
|
|
21
|
+
changed: true,
|
|
22
|
+
message: `Installed ${options.hook} hook at ${path}.`
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export async function uninstallHook(hook, options = {}) {
|
|
26
|
+
const cwd = options.cwd ?? process.cwd();
|
|
27
|
+
const path = await gitHookPath(hook, cwd);
|
|
28
|
+
const existing = await readOptional(path);
|
|
29
|
+
if (!existing) {
|
|
30
|
+
return {
|
|
31
|
+
hook,
|
|
32
|
+
path,
|
|
33
|
+
changed: false,
|
|
34
|
+
message: `No ${hook} hook found at ${path}.`
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (!existing.includes(marker) && !options.force) {
|
|
38
|
+
throw new Error(`Refusing to remove non-jester ${hook} hook at ${path}. Re-run with --force to remove it anyway.`);
|
|
39
|
+
}
|
|
40
|
+
await unlink(path);
|
|
41
|
+
return {
|
|
42
|
+
hook,
|
|
43
|
+
path,
|
|
44
|
+
changed: true,
|
|
45
|
+
message: `Removed ${hook} hook at ${path}.`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
export async function hookStatus(cwd = process.cwd()) {
|
|
49
|
+
return Promise.all(hookNames.map(async (hook) => {
|
|
50
|
+
const path = await gitHookPath(hook, cwd);
|
|
51
|
+
const existing = await readOptional(path);
|
|
52
|
+
const managed = Boolean(existing?.includes(marker));
|
|
53
|
+
return {
|
|
54
|
+
hook,
|
|
55
|
+
path,
|
|
56
|
+
changed: false,
|
|
57
|
+
message: existing ? managed ? "installed" : "occupied by another hook" : "not installed"
|
|
58
|
+
};
|
|
59
|
+
}));
|
|
60
|
+
}
|
|
61
|
+
export async function assertGitRepository(cwd = process.cwd()) {
|
|
62
|
+
try {
|
|
63
|
+
await execFileAsync("git", ["rev-parse", "--show-toplevel"], { cwd });
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
throw new Error("This command must be run inside a git repository.");
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
export function isHookName(value) {
|
|
70
|
+
return Boolean(value && hookNames.includes(value));
|
|
71
|
+
}
|
|
72
|
+
export function shellCommandPrefixForLocalCli(cliPath) {
|
|
73
|
+
return `node ${shellQuote(toShellPath(cliPath))}`;
|
|
74
|
+
}
|
|
75
|
+
export function shellQuote(value) {
|
|
76
|
+
return `'${value.replace(/'/g, "'\\''")}'`;
|
|
77
|
+
}
|
|
78
|
+
async function gitHookPath(hook, cwd) {
|
|
79
|
+
await assertGitRepository(cwd);
|
|
80
|
+
const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", `hooks/${hook}`], { cwd });
|
|
81
|
+
return resolve(cwd, stdout.trim());
|
|
82
|
+
}
|
|
83
|
+
async function readOptional(path) {
|
|
84
|
+
try {
|
|
85
|
+
return await readFile(path, "utf8");
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return undefined;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function renderHook(options) {
|
|
92
|
+
if (options.hook === "pre-commit") {
|
|
93
|
+
return renderPreCommitHook(options.commandPrefix, options.failOn);
|
|
94
|
+
}
|
|
95
|
+
return renderPrePushHook(options.commandPrefix, options.failOn);
|
|
96
|
+
}
|
|
97
|
+
function renderPreCommitHook(commandPrefix, failOn) {
|
|
98
|
+
return `#!/bin/sh
|
|
99
|
+
# ${marker}
|
|
100
|
+
set -eu
|
|
101
|
+
|
|
102
|
+
diff_output="$(git diff --cached --binary --no-ext-diff)"
|
|
103
|
+
if [ -z "$diff_output" ]; then
|
|
104
|
+
exit 0
|
|
105
|
+
fi
|
|
106
|
+
|
|
107
|
+
printf "%s\\n" "$diff_output" | ${commandPrefix} diff --fail-on ${failOn} --subject "staged changes"
|
|
108
|
+
`;
|
|
109
|
+
}
|
|
110
|
+
function renderPrePushHook(commandPrefix, failOn) {
|
|
111
|
+
return `#!/bin/sh
|
|
112
|
+
# ${marker}
|
|
113
|
+
set -eu
|
|
114
|
+
|
|
115
|
+
upstream="$(git rev-parse --abbrev-ref --symbolic-full-name @{upstream} 2>/dev/null || true)"
|
|
116
|
+
if [ -n "$upstream" ]; then
|
|
117
|
+
diff_range="$upstream..HEAD"
|
|
118
|
+
else
|
|
119
|
+
diff_range="HEAD~1..HEAD"
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
diff_output="$(git diff --binary --no-ext-diff "$diff_range" 2>/dev/null || git diff --binary --no-ext-diff HEAD 2>/dev/null || true)"
|
|
123
|
+
if [ -z "$diff_output" ]; then
|
|
124
|
+
exit 0
|
|
125
|
+
fi
|
|
126
|
+
|
|
127
|
+
printf "%s\\n" "$diff_output" | ${commandPrefix} diff --fail-on ${failOn} --subject "unpushed changes"
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
130
|
+
function toShellPath(path) {
|
|
131
|
+
return path.replace(/\\/g, "/");
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=hooks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"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"}
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
3
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { loadConfig } from "./config.js";
|
|
6
|
+
import { reviewCommand, reviewDiff, reviewFinalAnswer, reviewPlan } from "./core.js";
|
|
7
|
+
import { formatReview } from "./format.js";
|
|
8
|
+
import { tones } from "./types.js";
|
|
9
|
+
const toneSchema = z.enum(tones).optional();
|
|
10
|
+
const sharedShape = {
|
|
11
|
+
context: z.string().optional(),
|
|
12
|
+
tone: toneSchema,
|
|
13
|
+
intensity: z.number().int().min(1).max(5).optional(),
|
|
14
|
+
riskTolerance: z.enum(["low", "medium", "high"]).optional(),
|
|
15
|
+
configPath: z.string().optional(),
|
|
16
|
+
noConfig: z.boolean().optional()
|
|
17
|
+
};
|
|
18
|
+
const server = new McpServer({
|
|
19
|
+
name: "memento-mori-jester",
|
|
20
|
+
version: "0.1.0"
|
|
21
|
+
});
|
|
22
|
+
server.registerTool("jester_review_plan", {
|
|
23
|
+
title: "Review Agent Plan",
|
|
24
|
+
description: "Puncture overconfident plans with a brief jester critique and concrete verification checks.",
|
|
25
|
+
inputSchema: {
|
|
26
|
+
plan: z.string().min(1),
|
|
27
|
+
subject: z.string().optional(),
|
|
28
|
+
...sharedShape
|
|
29
|
+
}
|
|
30
|
+
}, async ({ plan, subject, context, tone, intensity, riskTolerance, configPath, noConfig }) => {
|
|
31
|
+
const { config } = await loadConfig({ configPath, search: !noConfig });
|
|
32
|
+
const result = reviewPlan(plan, { subject, context, tone, intensity, riskTolerance, config });
|
|
33
|
+
return toolResult(result);
|
|
34
|
+
});
|
|
35
|
+
server.registerTool("jester_check_command", {
|
|
36
|
+
title: "Check Shell Command",
|
|
37
|
+
description: "Review a shell command for destructive operations, broad file changes, and other footguns.",
|
|
38
|
+
inputSchema: {
|
|
39
|
+
command: z.string().min(1),
|
|
40
|
+
subject: z.string().optional(),
|
|
41
|
+
...sharedShape
|
|
42
|
+
}
|
|
43
|
+
}, async ({ command, subject, context, tone, intensity, riskTolerance, configPath, noConfig }) => {
|
|
44
|
+
const { config } = await loadConfig({ configPath, search: !noConfig });
|
|
45
|
+
const result = reviewCommand(command, { subject, context, tone, intensity, riskTolerance, config });
|
|
46
|
+
return toolResult(result);
|
|
47
|
+
});
|
|
48
|
+
server.registerTool("jester_review_diff", {
|
|
49
|
+
title: "Review Code Diff",
|
|
50
|
+
description: "Review a diff for risky deletions, suppressed types, debug logs, sensitive areas, and missing checks.",
|
|
51
|
+
inputSchema: {
|
|
52
|
+
diff: z.string().min(1),
|
|
53
|
+
subject: z.string().optional(),
|
|
54
|
+
...sharedShape
|
|
55
|
+
}
|
|
56
|
+
}, async ({ diff, subject, context, tone, intensity, riskTolerance, configPath, noConfig }) => {
|
|
57
|
+
const { config } = await loadConfig({ configPath, search: !noConfig });
|
|
58
|
+
const result = reviewDiff(diff, { subject, context, tone, intensity, riskTolerance, config });
|
|
59
|
+
return toolResult(result);
|
|
60
|
+
});
|
|
61
|
+
server.registerTool("jester_final_answer_roast", {
|
|
62
|
+
title: "Review Final Answer",
|
|
63
|
+
description: "Check whether a final agent answer overclaims, omits verification, or needs a humbling caveat.",
|
|
64
|
+
inputSchema: {
|
|
65
|
+
answer: z.string().min(1),
|
|
66
|
+
subject: z.string().optional(),
|
|
67
|
+
...sharedShape
|
|
68
|
+
}
|
|
69
|
+
}, async ({ answer, subject, context, tone, intensity, riskTolerance, configPath, noConfig }) => {
|
|
70
|
+
const { config } = await loadConfig({ configPath, search: !noConfig });
|
|
71
|
+
const result = reviewFinalAnswer(answer, { subject, context, tone, intensity, riskTolerance, config });
|
|
72
|
+
return toolResult(result);
|
|
73
|
+
});
|
|
74
|
+
const transport = new StdioServerTransport();
|
|
75
|
+
await server.connect(transport);
|
|
76
|
+
function toolResult(result) {
|
|
77
|
+
return {
|
|
78
|
+
content: [
|
|
79
|
+
{
|
|
80
|
+
type: "text",
|
|
81
|
+
text: formatReview(result)
|
|
82
|
+
}
|
|
83
|
+
],
|
|
84
|
+
structuredContent: result
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare const tones: readonly ["gentle_stoic", "court_jester", "absolute_menace", "professional"];
|
|
2
|
+
export type Tone = (typeof tones)[number];
|
|
3
|
+
export declare const reviewKinds: readonly ["plan", "command", "diff", "final"];
|
|
4
|
+
export type ReviewKind = (typeof reviewKinds)[number];
|
|
5
|
+
export type Verdict = "pass" | "caution" | "block";
|
|
6
|
+
export type RiskTolerance = "low" | "medium" | "high";
|
|
7
|
+
export type HookFailOn = "caution" | "block";
|
|
8
|
+
export interface JesterConfig {
|
|
9
|
+
tone: Tone;
|
|
10
|
+
intensity: number;
|
|
11
|
+
riskTolerance: RiskTolerance;
|
|
12
|
+
}
|
|
13
|
+
export interface CustomRuleConfig {
|
|
14
|
+
id: string;
|
|
15
|
+
pattern: string;
|
|
16
|
+
severity?: Issue["severity"];
|
|
17
|
+
title?: string;
|
|
18
|
+
detail?: string;
|
|
19
|
+
suggestedCheck?: string;
|
|
20
|
+
kinds?: ReviewKind[];
|
|
21
|
+
flags?: string;
|
|
22
|
+
}
|
|
23
|
+
export interface UserJesterConfig {
|
|
24
|
+
tone?: Tone;
|
|
25
|
+
intensity?: number;
|
|
26
|
+
riskTolerance?: RiskTolerance;
|
|
27
|
+
blockedCommands?: string[];
|
|
28
|
+
sensitiveDomains?: string[];
|
|
29
|
+
customRules?: CustomRuleConfig[];
|
|
30
|
+
hookFailOn?: HookFailOn;
|
|
31
|
+
}
|
|
32
|
+
export interface ReviewInput {
|
|
33
|
+
kind: ReviewKind;
|
|
34
|
+
content: string;
|
|
35
|
+
subject?: string;
|
|
36
|
+
context?: string;
|
|
37
|
+
tone?: Tone;
|
|
38
|
+
intensity?: number;
|
|
39
|
+
riskTolerance?: RiskTolerance;
|
|
40
|
+
config?: UserJesterConfig;
|
|
41
|
+
}
|
|
42
|
+
export interface Issue {
|
|
43
|
+
id: string;
|
|
44
|
+
severity: 1 | 2 | 3 | 4 | 5;
|
|
45
|
+
title: string;
|
|
46
|
+
detail: string;
|
|
47
|
+
suggestedCheck: string;
|
|
48
|
+
evidence?: string;
|
|
49
|
+
}
|
|
50
|
+
export interface ReviewResult {
|
|
51
|
+
kind: ReviewKind;
|
|
52
|
+
subject: string;
|
|
53
|
+
verdict: Verdict;
|
|
54
|
+
riskScore: number;
|
|
55
|
+
tone: Tone;
|
|
56
|
+
intensity: number;
|
|
57
|
+
jab: string;
|
|
58
|
+
memento: string;
|
|
59
|
+
issues: Issue[];
|
|
60
|
+
suggestedChecks: string[];
|
|
61
|
+
}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB,cAAc;IACd,cAAc;IACd,iBAAiB;IACjB,cAAc;CACN,CAAC;AAIX,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAU,CAAC"}
|
package/docs/AGENTS.md
ADDED
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# Agent Setup
|
|
2
|
+
|
|
3
|
+
Memento Mori Jester is an MCP stdio server plus a normal CLI. The safest pattern is:
|
|
4
|
+
|
|
5
|
+
1. Add the MCP server to the agent.
|
|
6
|
+
2. Add the suggested instruction to the agent's rules or custom instructions.
|
|
7
|
+
3. Keep a `jester.config.json` in project repos that need local rules.
|
|
8
|
+
|
|
9
|
+
## Quick MCP Config
|
|
10
|
+
|
|
11
|
+
After npm publish, use the `npx` config:
|
|
12
|
+
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"mcpServers": {
|
|
16
|
+
"memento-mori-jester": {
|
|
17
|
+
"command": "npx",
|
|
18
|
+
"args": [
|
|
19
|
+
"-y",
|
|
20
|
+
"memento-mori-jester@latest",
|
|
21
|
+
"mcp-server"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For a global install:
|
|
29
|
+
|
|
30
|
+
```json
|
|
31
|
+
{
|
|
32
|
+
"mcpServers": {
|
|
33
|
+
"memento-mori-jester": {
|
|
34
|
+
"command": "memento-mori-jester-mcp",
|
|
35
|
+
"args": []
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
For local development from this repo:
|
|
42
|
+
|
|
43
|
+
```powershell
|
|
44
|
+
npm.cmd install
|
|
45
|
+
npm.cmd run build
|
|
46
|
+
node .\dist\cli.js mcp-config --mode local
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Suggested Agent Instruction
|
|
50
|
+
|
|
51
|
+
```text
|
|
52
|
+
Before risky commands, final answers, commits, or large edits, call the Memento Mori Jester. Treat BLOCK as requiring a changed plan, and CAUTION as requiring at least one concrete verification step.
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Project Bootstrap
|
|
56
|
+
|
|
57
|
+
Use `bootstrap` inside a repo when you want the project files created for you:
|
|
58
|
+
|
|
59
|
+
```powershell
|
|
60
|
+
jester bootstrap --preset node
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Before npm publish, the GitHub `npx` path should keep using the GitHub package spec in generated MCP config:
|
|
64
|
+
|
|
65
|
+
```powershell
|
|
66
|
+
npx -y github:Martin123132/Memento-Mori bootstrap --preset node --package github:Martin123132/Memento-Mori
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
It writes `jester.config.json`, `memento-mori.mcp.json`, and `MEMENTO_MORI.md`. Existing files are kept unless `--force` is passed.
|
|
70
|
+
|
|
71
|
+
To add managed git hooks during bootstrap:
|
|
72
|
+
|
|
73
|
+
```powershell
|
|
74
|
+
jester bootstrap --preset node --hook pre-commit
|
|
75
|
+
jester bootstrap --preset security --hook pre-commit --hook pre-push
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Codex
|
|
79
|
+
|
|
80
|
+
Use:
|
|
81
|
+
|
|
82
|
+
```powershell
|
|
83
|
+
jester init --agent codex
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Then paste the generated `mcpServers` block wherever your Codex MCP configuration is kept. For local development before npm publish:
|
|
87
|
+
|
|
88
|
+
```powershell
|
|
89
|
+
node .\dist\cli.js init --mode local --agent codex
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Claude Code
|
|
93
|
+
|
|
94
|
+
Use:
|
|
95
|
+
|
|
96
|
+
```powershell
|
|
97
|
+
jester init --agent claude
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Then paste the generated `mcpServers` block into Claude Code's MCP configuration. If Claude Code asks for a command and args separately, keep the generated values exactly.
|
|
101
|
+
|
|
102
|
+
## Generic MCP Clients
|
|
103
|
+
|
|
104
|
+
Use:
|
|
105
|
+
|
|
106
|
+
```powershell
|
|
107
|
+
jester mcp-config --mode npx
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
If the client does not support `npx`, install globally:
|
|
111
|
+
|
|
112
|
+
```powershell
|
|
113
|
+
npm install -g memento-mori-jester
|
|
114
|
+
jester mcp-config --mode global
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
## Project Rules
|
|
118
|
+
|
|
119
|
+
Create a project config:
|
|
120
|
+
|
|
121
|
+
```powershell
|
|
122
|
+
jester config init
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Use a preset for common stacks:
|
|
126
|
+
|
|
127
|
+
```powershell
|
|
128
|
+
jester config init --preset node
|
|
129
|
+
jester config init --preset python
|
|
130
|
+
jester config init --preset security
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Validate config before asking agents or hooks to use it:
|
|
134
|
+
|
|
135
|
+
```powershell
|
|
136
|
+
jester config validate
|
|
137
|
+
jester config validate --json
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Agents do not need to pass the config manually. The MCP server searches upward from the current working directory for:
|
|
141
|
+
|
|
142
|
+
- `jester.config.json`
|
|
143
|
+
- `.jester.json`
|
|
144
|
+
|
|
145
|
+
If an agent invokes tools outside the project directory, pass `configPath` to the MCP tool.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
# GitHub Actions
|
|
2
|
+
|
|
3
|
+
Use Memento Mori Jester in CI to review diffs before they merge.
|
|
4
|
+
|
|
5
|
+
## Composite Action
|
|
6
|
+
|
|
7
|
+
This repo can be used directly as a GitHub Action:
|
|
8
|
+
|
|
9
|
+
```yaml
|
|
10
|
+
name: Jester Review
|
|
11
|
+
|
|
12
|
+
on:
|
|
13
|
+
pull_request:
|
|
14
|
+
branches: [main]
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
jester:
|
|
18
|
+
runs-on: ubuntu-latest
|
|
19
|
+
|
|
20
|
+
steps:
|
|
21
|
+
- name: Checkout
|
|
22
|
+
uses: actions/checkout@v4
|
|
23
|
+
with:
|
|
24
|
+
fetch-depth: 0
|
|
25
|
+
|
|
26
|
+
- name: Review diff
|
|
27
|
+
uses: Martin123132/Memento-Mori@main
|
|
28
|
+
with:
|
|
29
|
+
fail-on: block
|
|
30
|
+
subject: pull request diff
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
For pinned releases, replace `@main` with a tag such as `@v0.1.0`.
|
|
34
|
+
|
|
35
|
+
## Pull Request Diff Review
|
|
36
|
+
|
|
37
|
+
Create `.github/workflows/jester.yml`:
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
name: Jester Review
|
|
41
|
+
|
|
42
|
+
on:
|
|
43
|
+
pull_request:
|
|
44
|
+
branches: [main]
|
|
45
|
+
|
|
46
|
+
jobs:
|
|
47
|
+
jester:
|
|
48
|
+
runs-on: ubuntu-latest
|
|
49
|
+
|
|
50
|
+
steps:
|
|
51
|
+
- name: Checkout
|
|
52
|
+
uses: actions/checkout@v4
|
|
53
|
+
with:
|
|
54
|
+
fetch-depth: 0
|
|
55
|
+
|
|
56
|
+
- name: Review pull request diff
|
|
57
|
+
run: |
|
|
58
|
+
git fetch origin "${{ github.base_ref }}" --depth=1
|
|
59
|
+
git diff --binary --no-ext-diff "origin/${{ github.base_ref }}...HEAD" \
|
|
60
|
+
| npx -y memento-mori-jester@latest diff --fail-on block --subject "pull request diff"
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Push Diff Review
|
|
64
|
+
|
|
65
|
+
```yaml
|
|
66
|
+
name: Jester Push Review
|
|
67
|
+
|
|
68
|
+
on:
|
|
69
|
+
push:
|
|
70
|
+
branches: [main]
|
|
71
|
+
|
|
72
|
+
jobs:
|
|
73
|
+
jester:
|
|
74
|
+
runs-on: ubuntu-latest
|
|
75
|
+
|
|
76
|
+
steps:
|
|
77
|
+
- name: Checkout
|
|
78
|
+
uses: actions/checkout@v4
|
|
79
|
+
with:
|
|
80
|
+
fetch-depth: 2
|
|
81
|
+
|
|
82
|
+
- name: Review pushed diff
|
|
83
|
+
run: |
|
|
84
|
+
git diff --binary --no-ext-diff HEAD~1..HEAD \
|
|
85
|
+
| npx -y memento-mori-jester@latest diff --fail-on block --subject "pushed diff"
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## With Project Config
|
|
89
|
+
|
|
90
|
+
Commit `jester.config.json` at the repo root. The CLI discovers it automatically:
|
|
91
|
+
|
|
92
|
+
```yaml
|
|
93
|
+
- run: |
|
|
94
|
+
git diff --binary --no-ext-diff "origin/${{ github.base_ref }}...HEAD" \
|
|
95
|
+
| npx -y memento-mori-jester@latest diff --fail-on block
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
To use a non-root config:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
- run: |
|
|
102
|
+
git diff --binary --no-ext-diff "origin/${{ github.base_ref }}...HEAD" \
|
|
103
|
+
| npx -y memento-mori-jester@latest diff --config .github/jester.config.json --fail-on block
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Softer Mode
|
|
107
|
+
|
|
108
|
+
Use `--fail-on caution` if you want warnings to fail CI too:
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
- run: |
|
|
112
|
+
git diff --binary --no-ext-diff "origin/${{ github.base_ref }}...HEAD" \
|
|
113
|
+
| npx -y memento-mori-jester@latest diff --fail-on caution
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
Use `--json` if another CI step will parse the result.
|
package/docs/RELEASE.md
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# Release Guide
|
|
2
|
+
|
|
3
|
+
This project is ready to become a one-command package.
|
|
4
|
+
|
|
5
|
+
## 1. Create The Repo
|
|
6
|
+
|
|
7
|
+
Suggested repo name:
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
Memento-Mori
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
After creating it:
|
|
14
|
+
|
|
15
|
+
```powershell
|
|
16
|
+
git remote add origin https://github.com/Martin123132/Memento-Mori.git
|
|
17
|
+
git branch -M main
|
|
18
|
+
git push -u origin main
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## 2. Confirm Package Name
|
|
22
|
+
|
|
23
|
+
The name was available on 2026-05-17:
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
npm view memento-mori-jester version
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
An npm 404 means the name is still unclaimed.
|
|
30
|
+
|
|
31
|
+
## 3. Publish To npm
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
npm login
|
|
35
|
+
npm test
|
|
36
|
+
npm run pack:dry
|
|
37
|
+
npm publish
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
The package publishes these bins:
|
|
41
|
+
|
|
42
|
+
- `jester`: human CLI
|
|
43
|
+
- `memento-mori-jester`: human CLI, useful for `npx`
|
|
44
|
+
- `memento-mori-jester-mcp`: MCP stdio server
|
|
45
|
+
|
|
46
|
+
## 4. Smoke Test Published Package
|
|
47
|
+
|
|
48
|
+
```powershell
|
|
49
|
+
npx -y memento-mori-jester@latest doctor
|
|
50
|
+
npx -y memento-mori-jester@latest command "git reset --hard"
|
|
51
|
+
npx -y memento-mori-jester@latest init
|
|
52
|
+
npx -y memento-mori-jester@latest bootstrap --preset node
|
|
53
|
+
npx -y memento-mori-jester@latest config init
|
|
54
|
+
npx -y memento-mori-jester@latest config init --preset security --path jester-security.config.json
|
|
55
|
+
npx -y memento-mori-jester@latest config validate --config jester-security.config.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
If this machine is not logged in to npm yet:
|
|
59
|
+
|
|
60
|
+
```powershell
|
|
61
|
+
npm login
|
|
62
|
+
npm whoami
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Then run the publish commands again.
|
|
66
|
+
|
|
67
|
+
## 5. MCP Copy-Paste
|
|
68
|
+
|
|
69
|
+
The lowest-friction config uses `npx`:
|
|
70
|
+
|
|
71
|
+
```json
|
|
72
|
+
{
|
|
73
|
+
"mcpServers": {
|
|
74
|
+
"memento-mori-jester": {
|
|
75
|
+
"command": "npx",
|
|
76
|
+
"args": [
|
|
77
|
+
"-y",
|
|
78
|
+
"memento-mori-jester@latest",
|
|
79
|
+
"mcp-server"
|
|
80
|
+
]
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
For users who install globally:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"mcpServers": {
|
|
91
|
+
"memento-mori-jester": {
|
|
92
|
+
"command": "memento-mori-jester-mcp",
|
|
93
|
+
"args": []
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 6. Repository Metadata
|
|
100
|
+
|
|
101
|
+
The package is configured for:
|
|
102
|
+
|
|
103
|
+
```text
|
|
104
|
+
https://github.com/Martin123132/Memento-Mori
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
If the repo moves, update `repository`, `homepage`, and `bugs` in `package.json`, plus the raw installer URLs in `README.md`.
|
|
108
|
+
|
|
109
|
+
## 7. Post-Release Workflow Check
|
|
110
|
+
|
|
111
|
+
In a throwaway git repo:
|
|
112
|
+
|
|
113
|
+
```powershell
|
|
114
|
+
npx -y memento-mori-jester@latest config init
|
|
115
|
+
npx -y memento-mori-jester@latest install-hook pre-commit
|
|
116
|
+
npx -y memento-mori-jester@latest hook-status
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Then stage a risky diff and confirm the hook blocks or cautions according to `hookFailOn`.
|