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/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":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,YAAY,EAAE,UAAU,CAAU,CAAC;AAkB7D,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,MAAM,GAAG,kCAAkC,CAAC;AAElD,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAA2B;IAC3D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAClD,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IAE1C,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QAC7D,MAAM,IAAI,KAAK,CAAC,kCAAkC,OAAO,CAAC,IAAI,YAAY,IAAI,sCAAsC,CAAC,CAAC;IACxH,CAAC;IAED,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,CAAC;IACnD,MAAM,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAEzB,OAAO;QACL,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,IAAI;QACJ,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,aAAa,OAAO,CAAC,IAAI,YAAY,IAAI,GAAG;KACtD,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAc,EAAE,UAA6C,EAAE;IACjG,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACzC,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;IAE1C,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,MAAM,IAAI,kBAAkB,IAAI,GAAG;SAC7C,CAAC;IACJ,CAAC;IAED,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,iCAAiC,IAAI,YAAY,IAAI,4CAA4C,CAAC,CAAC;IACrH,CAAC;IAED,MAAM,MAAM,CAAC,IAAI,CAAC,CAAC;IAEnB,OAAO;QACL,IAAI;QACJ,IAAI;QACJ,OAAO,EAAE,IAAI;QACb,OAAO,EAAE,WAAW,IAAI,YAAY,IAAI,GAAG;KAC5C,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC1D,OAAO,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE;QAC9C,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;QAC1C,MAAM,QAAQ,GAAG,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;QAC1C,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC;QAEpD,OAAO;YACL,IAAI;YACJ,IAAI;YACJ,OAAO,EAAE,KAAK;YACd,OAAO,EAAE,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,0BAA0B,CAAC,CAAC,CAAC,eAAe;SACzF,CAAC;IACJ,CAAC,CAAC,CAAC,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IACnE,IAAI,CAAC;QACH,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,iBAAiB,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACxE,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;AACH,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAyB;IAClD,OAAO,OAAO,CAAC,KAAK,IAAI,SAAS,CAAC,QAAQ,CAAC,KAAiB,CAAC,CAAC,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,6BAA6B,CAAC,OAAe;IAC3D,OAAO,QAAQ,UAAU,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAa;IACtC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,CAAC;AAC7C,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,IAAc,EAAE,GAAW;IACpD,MAAM,mBAAmB,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,CAAC,WAAW,EAAE,YAAY,EAAE,SAAS,IAAI,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,CAAC,CAAC;IACrG,OAAO,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC;AACrC,CAAC;AAED,KAAK,UAAU,YAAY,CAAC,IAAY;IACtC,IAAI,CAAC;QACH,OAAO,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,OAA2B;IAC7C,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;QAClC,OAAO,mBAAmB,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IACpE,CAAC;IAED,OAAO,iBAAiB,CAAC,OAAO,CAAC,aAAa,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;AAClE,CAAC;AAED,SAAS,mBAAmB,CAAC,aAAqB,EAAE,MAAkB;IACpE,OAAO;IACL,MAAM;;;;;;;;kCAQwB,aAAa,mBAAmB,MAAM;CACvE,CAAC;AACF,CAAC;AAED,SAAS,iBAAiB,CAAC,aAAqB,EAAE,MAAkB;IAClE,OAAO;IACL,MAAM;;;;;;;;;;;;;;;kCAewB,aAAa,mBAAmB,MAAM;CACvE,CAAC;AACF,CAAC;AAED,SAAS,WAAW,CAAC,IAAY;IAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAClC,CAAC"}
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
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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"}
@@ -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,8 @@
1
+ export const tones = [
2
+ "gentle_stoic",
3
+ "court_jester",
4
+ "absolute_menace",
5
+ "professional"
6
+ ];
7
+ export const reviewKinds = ["plan", "command", "diff", "final"];
8
+ //# sourceMappingURL=types.js.map
@@ -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.
@@ -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`.