devguard 0.1.0

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 ADDED
@@ -0,0 +1,33 @@
1
+ # devGuard
2
+
3
+ MCP server that auto-generates dev diary entries from your git activity. What changed, what decisions were made, what broke, what's next — so you can pick up where you left off.
4
+
5
+ ## Why
6
+
7
+ You vibe code for 3 hours, close your laptop, and come back the next day with no idea what you were doing. devGuard fixes that. It reads your git state and writes a diary entry automatically.
8
+
9
+ ## Install
10
+
11
+ Add to Claude Code:
12
+
13
+ ```bash
14
+ claude mcp add devguard -- npx devguard
15
+ ```
16
+
17
+ That's it. On first run, devguard automatically:
18
+ - Adds `.devguard/` to your `.gitignore`
19
+ - Adds an auto-logging instruction to your `CLAUDE.md` (or `.cursorrules` if that exists)
20
+
21
+ From then on, your AI writes diary entries on its own — after finishing a feature, after a big commit, before context gets lost. You never think about it. The diary just fills itself.
22
+
23
+ ## Tools
24
+
25
+ | Tool | What it does |
26
+ |------|-------------|
27
+ | `get_context` | Reads git branch, status, recent commits, and diffs |
28
+ | `write_entry` | Saves a diary entry to `.devguard/entries/` |
29
+ | `read_entries` | Reads recent entries to catch you up |
30
+ | `catch_me_up` | Morning briefing — diary entries + git state in one shot |
31
+ | `setup` | Re-run setup manually if needed |
32
+
33
+ Entries are markdown files stored locally in your project under `.devguard/`, one file per day. Multiple sessions and agents all append to the same daily file.
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,32 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js");
5
+ const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js");
6
+ const get_context_js_1 = require("./tools/get-context.js");
7
+ const write_entry_js_1 = require("./tools/write-entry.js");
8
+ const read_entries_js_1 = require("./tools/read-entries.js");
9
+ const catch_me_up_js_1 = require("./tools/catch-me-up.js");
10
+ const setup_js_1 = require("./tools/setup.js");
11
+ const auto_setup_js_1 = require("./utils/auto-setup.js");
12
+ // Auto-setup on first run: adds .devguard/ to .gitignore
13
+ // and auto-logging instruction to CLAUDE.md or .cursorrules
14
+ (0, auto_setup_js_1.autoSetup)(process.cwd());
15
+ const server = new mcp_js_1.McpServer({
16
+ name: "devguard",
17
+ version: "0.1.0",
18
+ });
19
+ (0, get_context_js_1.registerGetContext)(server);
20
+ (0, write_entry_js_1.registerWriteEntry)(server);
21
+ (0, read_entries_js_1.registerReadEntries)(server);
22
+ (0, catch_me_up_js_1.registerCatchMeUp)(server);
23
+ (0, setup_js_1.registerSetup)(server);
24
+ async function main() {
25
+ const transport = new stdio_js_1.StdioServerTransport();
26
+ await server.connect(transport);
27
+ console.error("devguard MCP server running on stdio");
28
+ }
29
+ main().catch((err) => {
30
+ console.error("Fatal error:", err);
31
+ process.exit(1);
32
+ });
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerCatchMeUp(server: McpServer): void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerCatchMeUp = registerCatchMeUp;
37
+ const zod_1 = require("zod");
38
+ const git = __importStar(require("../utils/git.js"));
39
+ const storage = __importStar(require("../utils/storage.js"));
40
+ function registerCatchMeUp(server) {
41
+ server.tool("catch_me_up", "The morning briefing. Reads your recent diary entries and current git state, then returns everything the AI needs to give you a conversational catch-up — like a teammate who was watching over your shoulder.", {
42
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
43
+ }, async ({ project_path }) => {
44
+ const parts = [];
45
+ // Recent diary entries
46
+ const entries = storage.readEntries(project_path, 3);
47
+ if (entries.length > 0) {
48
+ parts.push("## Recent Diary Entries\n");
49
+ entries.forEach((entry, i) => {
50
+ parts.push(`### ${i === 0 ? "Latest" : `Entry ${i + 1}`}\n${entry}`);
51
+ });
52
+ }
53
+ else {
54
+ parts.push("## No previous diary entries found.\n");
55
+ }
56
+ // Current git state
57
+ if (git.isGitRepo(project_path)) {
58
+ const branch = git.getBranch(project_path);
59
+ const status = git.getStatus(project_path);
60
+ const commits = git.getRecentCommits(project_path, 5);
61
+ const diff = git.getDiffFull(project_path);
62
+ parts.push(`## Current Git State`, `**Branch:** ${branch}`, `**Working tree:**\n${status || "Clean"}`, `**Recent commits:**\n${commits || "None"}`, `**Uncommitted changes:**\n${diff}`);
63
+ }
64
+ parts.push("", "---", "Use all of the above to give the user a conversational catch-up briefing. Be specific — mention file names, branch names, what was last worked on, and where they should probably start today. Talk like a teammate, not a changelog.");
65
+ return {
66
+ content: [{ type: "text", text: parts.join("\n\n") }],
67
+ };
68
+ });
69
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerGetContext(server: McpServer): void;
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerGetContext = registerGetContext;
37
+ const zod_1 = require("zod");
38
+ const git = __importStar(require("../utils/git.js"));
39
+ function registerGetContext(server) {
40
+ server.tool("get_context", "Get the current git state of a project — branch, status, recent commits, and diffs. Call this to understand what happened before writing a diary entry.", {
41
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
42
+ }, async ({ project_path }) => {
43
+ if (!git.isGitRepo(project_path)) {
44
+ return {
45
+ content: [
46
+ {
47
+ type: "text",
48
+ text: `${project_path} is not a git repository. Dev diary works best with git projects.`,
49
+ },
50
+ ],
51
+ };
52
+ }
53
+ const branch = git.getBranch(project_path);
54
+ const status = git.getStatus(project_path);
55
+ const commits = git.getRecentCommits(project_path);
56
+ const diffSummary = git.getDiffSummary(project_path);
57
+ const diffFull = git.getDiffFull(project_path);
58
+ const output = [
59
+ `## Branch\n${branch}`,
60
+ `## Status\n${status || "Clean working tree"}`,
61
+ `## Recent Commits\n${commits || "No commits yet"}`,
62
+ `## Diff Summary\n${diffSummary}`,
63
+ `## Full Diff\n${diffFull}`,
64
+ ].join("\n\n");
65
+ return {
66
+ content: [{ type: "text", text: output }],
67
+ };
68
+ });
69
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerReadEntries(server: McpServer): void;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerReadEntries = registerReadEntries;
37
+ const zod_1 = require("zod");
38
+ const storage = __importStar(require("../utils/storage.js"));
39
+ function registerReadEntries(server) {
40
+ server.tool("read_entries", "Read recent dev diary entries. Call this at the start of a session to catch up on what happened last time.", {
41
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
42
+ count: zod_1.z
43
+ .number()
44
+ .int()
45
+ .positive()
46
+ .default(5)
47
+ .describe("Number of recent entries to return (default 5)"),
48
+ }, async ({ project_path, count }) => {
49
+ const entries = storage.readEntries(project_path, count);
50
+ if (entries.length === 0) {
51
+ return {
52
+ content: [
53
+ {
54
+ type: "text",
55
+ text: "No diary entries found. Start a session and write your first entry!",
56
+ },
57
+ ],
58
+ };
59
+ }
60
+ const output = entries
61
+ .map((entry, i) => `${i === 0 ? "## Latest Entry" : `## Entry ${i + 1}`}\n\n${entry}`)
62
+ .join("\n\n---\n\n");
63
+ return {
64
+ content: [{ type: "text", text: output }],
65
+ };
66
+ });
67
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerSetup(server: McpServer): void;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerSetup = registerSetup;
4
+ const zod_1 = require("zod");
5
+ const fs_1 = require("fs");
6
+ const path_1 = require("path");
7
+ const INSTRUCTION = `After completing a task, switching branches, or making a significant commit, call the devguard write_entry tool to log what was done and what's next.`;
8
+ const CONFIG_FILES = ["CLAUDE.md", ".cursorrules"];
9
+ function hasInstruction(content) {
10
+ return content.includes("devguard write_entry");
11
+ }
12
+ function registerSetup(server) {
13
+ server.tool("setup", "Add devguard's auto-logging instruction to your project's CLAUDE.md or .cursorrules. Run once per project — it appends the instruction so your AI writes diary entries automatically.", {
14
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
15
+ target: zod_1.z
16
+ .enum(["CLAUDE.md", ".cursorrules", "auto"])
17
+ .default("auto")
18
+ .describe("Which config file to add the instruction to. 'auto' picks whichever already exists, preferring CLAUDE.md."),
19
+ }, async ({ project_path, target }) => {
20
+ let targetFile;
21
+ if (target === "auto") {
22
+ // Prefer whichever already exists; default to CLAUDE.md
23
+ if ((0, fs_1.existsSync)((0, path_1.join)(project_path, "CLAUDE.md"))) {
24
+ targetFile = "CLAUDE.md";
25
+ }
26
+ else if ((0, fs_1.existsSync)((0, path_1.join)(project_path, ".cursorrules"))) {
27
+ targetFile = ".cursorrules";
28
+ }
29
+ else {
30
+ targetFile = "CLAUDE.md";
31
+ }
32
+ }
33
+ else {
34
+ targetFile = target;
35
+ }
36
+ const filePath = (0, path_1.join)(project_path, targetFile);
37
+ // Check if already set up
38
+ if ((0, fs_1.existsSync)(filePath)) {
39
+ const existing = (0, fs_1.readFileSync)(filePath, "utf-8");
40
+ if (hasInstruction(existing)) {
41
+ return {
42
+ content: [
43
+ {
44
+ type: "text",
45
+ text: `Already set up — ${targetFile} already contains the devguard instruction.`,
46
+ },
47
+ ],
48
+ };
49
+ }
50
+ // Append to existing file
51
+ const section = `\n\n# Dev Diary\n\n${INSTRUCTION}\n`;
52
+ (0, fs_1.appendFileSync)(filePath, section, "utf-8");
53
+ }
54
+ else {
55
+ // Create new file
56
+ const content = `# Dev Diary\n\n${INSTRUCTION}\n`;
57
+ (0, fs_1.writeFileSync)(filePath, content, "utf-8");
58
+ }
59
+ return {
60
+ content: [
61
+ {
62
+ type: "text",
63
+ text: `Done — added devguard instruction to ${targetFile}. Your AI will now log diary entries automatically.`,
64
+ },
65
+ ],
66
+ };
67
+ });
68
+ }
@@ -0,0 +1,2 @@
1
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerWriteEntry(server: McpServer): void;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.registerWriteEntry = registerWriteEntry;
37
+ const zod_1 = require("zod");
38
+ const git = __importStar(require("../utils/git.js"));
39
+ const storage = __importStar(require("../utils/storage.js"));
40
+ function registerWriteEntry(server) {
41
+ server.tool("write_entry", "Save a dev diary entry. Captures what changed, decisions made, issues hit, and what's next. Call get_context first to gather the raw material.", {
42
+ project_path: zod_1.z.string().describe("Absolute path to the project directory"),
43
+ summary: zod_1.z.string().describe("One-line summary of what happened this session"),
44
+ changes: zod_1.z.string().describe("What changed — files modified, features added, bugs fixed"),
45
+ decisions: zod_1.z
46
+ .string()
47
+ .optional()
48
+ .describe("Key decisions made and why"),
49
+ issues: zod_1.z
50
+ .string()
51
+ .optional()
52
+ .describe("What broke, what's stuck, errors encountered"),
53
+ next_steps: zod_1.z
54
+ .string()
55
+ .optional()
56
+ .describe("What to do next session"),
57
+ }, async ({ project_path, summary, changes, decisions, issues, next_steps }) => {
58
+ const branch = git.isGitRepo(project_path)
59
+ ? git.getBranch(project_path)
60
+ : "unknown";
61
+ const now = new Date().toISOString();
62
+ const sections = [
63
+ `---`,
64
+ `date: ${now}`,
65
+ `branch: ${branch}`,
66
+ `summary: "${summary}"`,
67
+ `---`,
68
+ "",
69
+ `# ${summary}`,
70
+ "",
71
+ `## What Changed`,
72
+ changes,
73
+ ];
74
+ if (decisions) {
75
+ sections.push("", "## Decisions", decisions);
76
+ }
77
+ if (issues) {
78
+ sections.push("", "## Issues", issues);
79
+ }
80
+ if (next_steps) {
81
+ sections.push("", "## Next Steps", next_steps);
82
+ }
83
+ const content = sections.join("\n") + "\n";
84
+ const filePath = storage.writeEntry(project_path, content);
85
+ return {
86
+ content: [
87
+ {
88
+ type: "text",
89
+ text: `Diary entry saved: ${filePath}`,
90
+ },
91
+ ],
92
+ };
93
+ });
94
+ }
@@ -0,0 +1 @@
1
+ export declare function autoSetup(projectPath: string): void;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.autoSetup = autoSetup;
4
+ const fs_1 = require("fs");
5
+ const path_1 = require("path");
6
+ const GITIGNORE_ENTRY = ".devguard/";
7
+ const INSTRUCTION = `After completing a task, switching branches, or making a significant commit, call the devguard write_entry tool to log what was done and what's next.`;
8
+ function autoSetup(projectPath) {
9
+ addToGitignore(projectPath);
10
+ addInstruction(projectPath);
11
+ }
12
+ function addToGitignore(projectPath) {
13
+ const gitignorePath = (0, path_1.join)(projectPath, ".gitignore");
14
+ if ((0, fs_1.existsSync)(gitignorePath)) {
15
+ const content = (0, fs_1.readFileSync)(gitignorePath, "utf-8");
16
+ if (content.includes(GITIGNORE_ENTRY))
17
+ return;
18
+ (0, fs_1.appendFileSync)(gitignorePath, `\n${GITIGNORE_ENTRY}\n`, "utf-8");
19
+ }
20
+ else {
21
+ (0, fs_1.writeFileSync)(gitignorePath, `${GITIGNORE_ENTRY}\n`, "utf-8");
22
+ }
23
+ console.error(`devguard: added ${GITIGNORE_ENTRY} to .gitignore`);
24
+ }
25
+ function addInstruction(projectPath) {
26
+ const claudeMd = (0, path_1.join)(projectPath, "CLAUDE.md");
27
+ const cursorrules = (0, path_1.join)(projectPath, ".cursorrules");
28
+ // Pick target: prefer whichever exists, default to CLAUDE.md
29
+ let targetPath;
30
+ let targetName;
31
+ if ((0, fs_1.existsSync)(claudeMd)) {
32
+ targetPath = claudeMd;
33
+ targetName = "CLAUDE.md";
34
+ }
35
+ else if ((0, fs_1.existsSync)(cursorrules)) {
36
+ targetPath = cursorrules;
37
+ targetName = ".cursorrules";
38
+ }
39
+ else {
40
+ targetPath = claudeMd;
41
+ targetName = "CLAUDE.md";
42
+ }
43
+ // Already has it?
44
+ if ((0, fs_1.existsSync)(targetPath)) {
45
+ const content = (0, fs_1.readFileSync)(targetPath, "utf-8");
46
+ if (content.includes("devguard write_entry"))
47
+ return;
48
+ (0, fs_1.appendFileSync)(targetPath, `\n\n# Dev Diary\n\n${INSTRUCTION}\n`, "utf-8");
49
+ }
50
+ else {
51
+ (0, fs_1.writeFileSync)(targetPath, `# Dev Diary\n\n${INSTRUCTION}\n`, "utf-8");
52
+ }
53
+ console.error(`devguard: added auto-logging instruction to ${targetName}`);
54
+ }
@@ -0,0 +1,6 @@
1
+ export declare function isGitRepo(cwd: string): boolean;
2
+ export declare function getBranch(cwd: string): string;
3
+ export declare function getStatus(cwd: string): string;
4
+ export declare function getRecentCommits(cwd: string, count?: number): string;
5
+ export declare function getDiffSummary(cwd: string): string;
6
+ export declare function getDiffFull(cwd: string): string;
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isGitRepo = isGitRepo;
4
+ exports.getBranch = getBranch;
5
+ exports.getStatus = getStatus;
6
+ exports.getRecentCommits = getRecentCommits;
7
+ exports.getDiffSummary = getDiffSummary;
8
+ exports.getDiffFull = getDiffFull;
9
+ const child_process_1 = require("child_process");
10
+ function run(cmd, cwd) {
11
+ try {
12
+ return (0, child_process_1.execSync)(cmd, { cwd, encoding: "utf-8", timeout: 10_000 }).trim();
13
+ }
14
+ catch {
15
+ return "";
16
+ }
17
+ }
18
+ function isGitRepo(cwd) {
19
+ return run("git rev-parse --is-inside-work-tree", cwd) === "true";
20
+ }
21
+ function getBranch(cwd) {
22
+ return run("git branch --show-current", cwd) || "detached HEAD";
23
+ }
24
+ function getStatus(cwd) {
25
+ return run("git status --short", cwd);
26
+ }
27
+ function getRecentCommits(cwd, count = 10) {
28
+ return run(`git log --oneline --no-decorate -n ${count}`, cwd);
29
+ }
30
+ function getDiffSummary(cwd) {
31
+ const staged = run("git diff --cached --stat", cwd);
32
+ const unstaged = run("git diff --stat", cwd);
33
+ const parts = [];
34
+ if (staged)
35
+ parts.push(`Staged:\n${staged}`);
36
+ if (unstaged)
37
+ parts.push(`Unstaged:\n${unstaged}`);
38
+ return parts.join("\n\n") || "No changes";
39
+ }
40
+ function getDiffFull(cwd) {
41
+ const staged = run("git diff --cached", cwd);
42
+ const unstaged = run("git diff", cwd);
43
+ const parts = [];
44
+ if (staged)
45
+ parts.push(`--- Staged changes ---\n${staged}`);
46
+ if (unstaged)
47
+ parts.push(`--- Unstaged changes ---\n${unstaged}`);
48
+ // Truncate to avoid overwhelming context
49
+ const combined = parts.join("\n\n") || "No changes";
50
+ if (combined.length > 8000) {
51
+ return combined.slice(0, 8000) + "\n\n... (truncated, use diff summary for full picture)";
52
+ }
53
+ return combined;
54
+ }
@@ -0,0 +1,3 @@
1
+ export declare function ensureDiaryDir(projectPath: string): string;
2
+ export declare function writeEntry(projectPath: string, content: string): string;
3
+ export declare function readEntries(projectPath: string, count: number): string[];
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ensureDiaryDir = ensureDiaryDir;
4
+ exports.writeEntry = writeEntry;
5
+ exports.readEntries = readEntries;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const DIARY_DIR = ".devguard";
9
+ const ENTRIES_DIR = "entries";
10
+ function entriesPath(projectPath) {
11
+ return (0, path_1.join)(projectPath, DIARY_DIR, ENTRIES_DIR);
12
+ }
13
+ function ensureDiaryDir(projectPath) {
14
+ const dir = entriesPath(projectPath);
15
+ (0, fs_1.mkdirSync)(dir, { recursive: true });
16
+ return dir;
17
+ }
18
+ function writeEntry(projectPath, content) {
19
+ const dir = ensureDiaryDir(projectPath);
20
+ const now = new Date();
21
+ const filename = formatDate(now) + ".md";
22
+ const filePath = (0, path_1.join)(dir, filename);
23
+ const time = formatTime(now);
24
+ const separator = `\n\n---\n\n<!-- session: ${time} -->\n\n`;
25
+ if ((0, fs_1.existsSync)(filePath)) {
26
+ (0, fs_1.appendFileSync)(filePath, separator + content, "utf-8");
27
+ }
28
+ else {
29
+ (0, fs_1.writeFileSync)(filePath, content, "utf-8");
30
+ }
31
+ return filePath;
32
+ }
33
+ function readEntries(projectPath, count) {
34
+ const dir = entriesPath(projectPath);
35
+ if (!(0, fs_1.existsSync)(dir))
36
+ return [];
37
+ const files = (0, fs_1.readdirSync)(dir)
38
+ .filter((f) => f.endsWith(".md"))
39
+ .sort()
40
+ .reverse()
41
+ .slice(0, count);
42
+ return files.map((f) => (0, fs_1.readFileSync)((0, path_1.join)(dir, f), "utf-8"));
43
+ }
44
+ function formatDate(date) {
45
+ const y = date.getFullYear();
46
+ const mo = String(date.getMonth() + 1).padStart(2, "0");
47
+ const d = String(date.getDate()).padStart(2, "0");
48
+ return `${y}-${mo}-${d}`;
49
+ }
50
+ function formatTime(date) {
51
+ const h = String(date.getHours()).padStart(2, "0");
52
+ const mi = String(date.getMinutes()).padStart(2, "0");
53
+ return `${h}:${mi}`;
54
+ }
package/package.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "devguard",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that auto-generates dev diary entries from git activity",
5
+ "license": "MIT",
6
+ "bin": {
7
+ "devguard": "./dist/index.js"
8
+ },
9
+ "files": [
10
+ "dist",
11
+ "README.md"
12
+ ],
13
+ "scripts": {
14
+ "dev": "tsx watch src/index.ts",
15
+ "build": "tsc -p tsconfig.json",
16
+ "start": "node dist/index.js",
17
+ "prepublishOnly": "npm run build",
18
+ "inspect": "npx @modelcontextprotocol/inspector tsx src/index.ts"
19
+ },
20
+ "dependencies": {
21
+ "@modelcontextprotocol/sdk": "^1.12.0",
22
+ "zod": "^3.24.0"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^22.0.0",
26
+ "tsx": "^4.19.0",
27
+ "typescript": "^5.7.0"
28
+ }
29
+ }