adhdev 0.9.62 → 0.9.64

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adhdev",
3
- "version": "0.9.62",
3
+ "version": "0.9.64",
4
4
  "description": "ADHDev — Agent Dashboard Hub for Dev. Remote-control AI coding agents from anywhere.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -153,6 +153,55 @@ var CloudTransport = class {
153
153
  if (!res.ok) throw new Error(`Launch failed: ${res.status}`);
154
154
  return res.json();
155
155
  }
156
+ async gitLog(daemonId, workspace, opts = {}) {
157
+ const params = new URLSearchParams({ workspace });
158
+ if (opts.limit) params.set("limit", String(opts.limit));
159
+ if (opts.file) params.set("file", opts.file);
160
+ if (opts.since) params.set("since", opts.since);
161
+ if (opts.until) params.set("until", opts.until);
162
+ const res = await fetch(
163
+ `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-log?${params}`,
164
+ { headers: this.headers() }
165
+ );
166
+ if (!res.ok) throw new Error(`Git log failed: ${res.status}`);
167
+ return res.json();
168
+ }
169
+ async gitDiff(daemonId, workspace, opts = {}) {
170
+ const params = new URLSearchParams({ workspace });
171
+ if (opts.file) params.set("file", opts.file);
172
+ if (opts.maxLines) params.set("maxLines", String(opts.maxLines));
173
+ if (opts.staged) params.set("staged", "true");
174
+ const res = await fetch(
175
+ `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-diff?${params}`,
176
+ { headers: this.headers() }
177
+ );
178
+ if (!res.ok) throw new Error(`Git diff failed: ${res.status}`);
179
+ return res.json();
180
+ }
181
+ async gitPush(daemonId, opts) {
182
+ const res = await fetch(
183
+ `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-push`,
184
+ {
185
+ method: "POST",
186
+ headers: this.headers(),
187
+ body: JSON.stringify(opts)
188
+ }
189
+ );
190
+ if (!res.ok) throw new Error(`Git push failed: ${res.status}`);
191
+ return res.json();
192
+ }
193
+ async gitCheckpoint(daemonId, opts) {
194
+ const res = await fetch(
195
+ `${this.baseUrl}/api/v1/shortcuts/${encodeURIComponent(daemonId)}/git-checkpoint`,
196
+ {
197
+ method: "POST",
198
+ headers: this.headers(),
199
+ body: JSON.stringify(opts)
200
+ }
201
+ );
202
+ if (!res.ok) throw new Error(`Git checkpoint failed: ${res.status}`);
203
+ return res.json();
204
+ }
156
205
  async ping() {
157
206
  try {
158
207
  await this.listDaemons();
@@ -163,6 +212,11 @@ var CloudTransport = class {
163
212
  }
164
213
  };
165
214
 
215
+ // src/transports/mode.ts
216
+ function isLocalTransport(transport) {
217
+ return typeof transport.command === "function";
218
+ }
219
+
166
220
  // src/tools/list-sessions.ts
167
221
  var FORMAT_PROP = {
168
222
  format: {
@@ -188,7 +242,7 @@ var LIST_SESSIONS_TOOL = {
188
242
  };
189
243
  async function listSessions(transport, args = {}) {
190
244
  const asJson = args.format === "json";
191
- if ("getStatus" in transport) {
245
+ if (isLocalTransport(transport)) {
192
246
  const status = await transport.getStatus();
193
247
  const sessions = status?.sessions ?? [];
194
248
  if (asJson) {
@@ -279,7 +333,7 @@ var LIST_DAEMONS_TOOL = {
279
333
  };
280
334
  async function listDaemons(transport, args = {}) {
281
335
  const asJson = args.format === "json";
282
- if ("getStatus" in transport) {
336
+ if (isLocalTransport(transport)) {
283
337
  const status = await transport.getStatus();
284
338
  const daemon = {
285
339
  id: status?.id ?? status?.instanceId ?? "standalone",
@@ -347,7 +401,7 @@ var READ_CHAT_TOOL = {
347
401
  };
348
402
  async function readChat(transport, args) {
349
403
  const limit = args.limit ?? 50;
350
- if ("command" in transport) {
404
+ if (isLocalTransport(transport)) {
351
405
  const result2 = await transport.command("read_chat", {
352
406
  ...args.session_id ? { targetSessionId: args.session_id } : {},
353
407
  limit
@@ -411,7 +465,7 @@ var SEND_CHAT_TOOL = {
411
465
  };
412
466
  async function sendChat(transport, args) {
413
467
  if (!args.message?.trim()) throw new Error("message is required");
414
- if ("command" in transport) {
468
+ if (isLocalTransport(transport)) {
415
469
  const result2 = await transport.command("send_chat", {
416
470
  message: args.message,
417
471
  ...args.session_id ? { targetSessionId: args.session_id } : {}
@@ -454,7 +508,7 @@ var APPROVE_TOOL = {
454
508
  };
455
509
  async function approve(transport, args) {
456
510
  const action = args.action === "reject" ? "reject" : "approve";
457
- if ("command" in transport) {
511
+ if (isLocalTransport(transport)) {
458
512
  const result2 = await transport.command("resolve_action", {
459
513
  action,
460
514
  ...args.session_id ? { targetSessionId: args.session_id } : {}
@@ -486,7 +540,7 @@ var SCREENSHOT_TOOL = {
486
540
  };
487
541
  async function screenshot(transport, args) {
488
542
  let result;
489
- if ("command" in transport) {
543
+ if (isLocalTransport(transport)) {
490
544
  result = await transport.command("screenshot", {
491
545
  ...args.session_id ? { targetSessionId: args.session_id } : {}
492
546
  });
@@ -531,7 +585,7 @@ var GIT_STATUS_TOOL = {
531
585
  async function gitStatus(transport, args) {
532
586
  let status;
533
587
  let diffSummary;
534
- if ("command" in transport) {
588
+ if (isLocalTransport(transport)) {
535
589
  const statusResult = await transport.command("git_status", {
536
590
  workspace: args.workspace
537
591
  });
@@ -619,6 +673,372 @@ async function gitStatus(transport, args) {
619
673
  return lines.join("\n");
620
674
  }
621
675
 
676
+ // src/tools/git-log.ts
677
+ var GIT_LOG_TOOL = {
678
+ name: "git_log",
679
+ description: "Get commit history for a workspace. Shows hash, message, author, and date for recent commits. Use this to track what changes an agent has made, verify checkpoint commits, or understand project history.",
680
+ inputSchema: {
681
+ type: "object",
682
+ properties: {
683
+ workspace: {
684
+ type: "string",
685
+ description: "Absolute path to the workspace/repository directory."
686
+ },
687
+ limit: {
688
+ type: "number",
689
+ description: "Max commits to return (default: 20, max: 100)."
690
+ },
691
+ file: {
692
+ type: "string",
693
+ description: "Filter history to commits that touched this repo-relative file path (optional)."
694
+ },
695
+ since: {
696
+ type: "string",
697
+ description: "Only commits after this date (ISO 8601 or git date string, optional)."
698
+ },
699
+ until: {
700
+ type: "string",
701
+ description: "Only commits before this date (ISO 8601 or git date string, optional)."
702
+ },
703
+ daemon_id: {
704
+ type: "string",
705
+ description: "Daemon ID (cloud mode only, required)."
706
+ },
707
+ ...FORMAT_PROP
708
+ },
709
+ required: ["workspace"]
710
+ }
711
+ };
712
+ async function gitLog(transport, args) {
713
+ const limit = Math.max(1, Math.min(100, args.limit ?? 20));
714
+ let raw;
715
+ if (isLocalTransport(transport)) {
716
+ raw = await transport.command("git_log", {
717
+ workspace: args.workspace,
718
+ limit,
719
+ ...args.file ? { path: args.file } : {},
720
+ ...args.since ? { since: args.since } : {},
721
+ ...args.until ? { until: args.until } : {}
722
+ });
723
+ raw = raw?.log ?? raw;
724
+ } else {
725
+ if (!args.daemon_id) throw new Error("daemon_id is required in cloud mode");
726
+ const result = await transport.gitLog(args.daemon_id, args.workspace, {
727
+ limit,
728
+ file: args.file,
729
+ since: args.since,
730
+ until: args.until
731
+ });
732
+ raw = result?.log ?? result;
733
+ }
734
+ if (raw?.success === false || raw?.reason) {
735
+ const msg = raw?.error ?? raw?.reason ?? "unknown";
736
+ if (args.format === "json") return JSON.stringify({ error: msg }, null, 2);
737
+ return `Git log error: ${msg}`;
738
+ }
739
+ if (!raw?.isGitRepo) {
740
+ const msg = `Not a git repository: ${args.workspace}`;
741
+ if (args.format === "json") return JSON.stringify({ error: msg }, null, 2);
742
+ return msg;
743
+ }
744
+ const entries = raw?.entries ?? [];
745
+ if (args.format === "json") {
746
+ return JSON.stringify({
747
+ workspace: raw.workspace,
748
+ branch: raw.branch ?? null,
749
+ entries: entries.map((e) => ({
750
+ commit: e.commit,
751
+ short: e.commit?.slice(0, 7),
752
+ message: e.message,
753
+ author: e.authorName ?? null,
754
+ author_email: e.authorEmail ?? null,
755
+ authored_at: e.authoredAt ? new Date(e.authoredAt).toISOString() : null
756
+ })),
757
+ total: entries.length,
758
+ truncated: raw.truncated ?? false
759
+ }, null, 2);
760
+ }
761
+ if (entries.length === 0) return "No commits found.";
762
+ const lines = entries.map((e) => {
763
+ const hash = e.commit?.slice(0, 7) ?? "???????";
764
+ const date = e.authoredAt ? new Date(e.authoredAt).toISOString().slice(0, 10) : "";
765
+ const author = e.authorName ? ` (${e.authorName})` : "";
766
+ return `${hash} ${date}${author} ${e.message}`;
767
+ });
768
+ const header = `Commits (${entries.length}${raw.truncated ? ", truncated" : ""}):`;
769
+ return `${header}
770
+ ${lines.join("\n")}`;
771
+ }
772
+
773
+ // src/tools/git-diff.ts
774
+ var GIT_DIFF_TOOL = {
775
+ name: "git_diff",
776
+ description: "Get the actual diff content for changed files in a workspace. Without a specific file, returns diffs for up to 5 changed files. Use this to review what an agent actually changed \u2014 file names alone (from git_status) are not enough for code review.",
777
+ inputSchema: {
778
+ type: "object",
779
+ properties: {
780
+ workspace: {
781
+ type: "string",
782
+ description: "Absolute path to the workspace/repository directory."
783
+ },
784
+ file: {
785
+ type: "string",
786
+ description: "Specific repo-relative file path to diff (optional \u2014 if omitted, returns top 5 changed files)."
787
+ },
788
+ max_lines: {
789
+ type: "number",
790
+ description: "Max diff lines per file before truncating (default: 300)."
791
+ },
792
+ staged: {
793
+ type: "boolean",
794
+ description: "Show staged changes instead of unstaged (default: false)."
795
+ },
796
+ daemon_id: {
797
+ type: "string",
798
+ description: "Daemon ID (cloud mode only, required)."
799
+ },
800
+ ...FORMAT_PROP
801
+ },
802
+ required: ["workspace"]
803
+ }
804
+ };
805
+ async function gitDiff(transport, args) {
806
+ const maxLines = Math.max(10, Math.min(2e3, args.max_lines ?? 300));
807
+ const staged = args.staged ?? false;
808
+ if (isLocalTransport(transport)) {
809
+ return localGitDiff(transport, args.workspace, args.file, maxLines, staged, args.format);
810
+ }
811
+ if (!args.daemon_id) throw new Error("daemon_id is required in cloud mode");
812
+ const result = await transport.gitDiff(args.daemon_id, args.workspace, {
813
+ file: args.file,
814
+ maxLines,
815
+ staged
816
+ });
817
+ if (result?.error) {
818
+ if (args.format === "json") return JSON.stringify({ error: result.error }, null, 2);
819
+ return `Git diff error: ${result.error}`;
820
+ }
821
+ return formatDiffResult(result, args.format);
822
+ }
823
+ async function localGitDiff(transport, workspace, file, maxLines, staged, format) {
824
+ if (file) {
825
+ const raw = await transport.command("git_diff_file", { workspace, path: file, staged });
826
+ const d = raw?.diff ?? raw;
827
+ if (d?.success === false || d?.reason) {
828
+ const msg = d?.error ?? d?.reason ?? "unknown";
829
+ if (format === "json") return JSON.stringify({ error: msg }, null, 2);
830
+ return `Git diff error: ${msg}`;
831
+ }
832
+ const lines = (d?.diff ?? "").split("\n");
833
+ const truncated = lines.length > maxLines;
834
+ const result = {
835
+ files: [{
836
+ path: file,
837
+ diff: truncated ? lines.slice(0, maxLines).join("\n") + "\n... (truncated)" : d?.diff ?? "",
838
+ truncated,
839
+ binary: d?.binary ?? false
840
+ }],
841
+ total_files: 1,
842
+ shown_files: 1,
843
+ truncated
844
+ };
845
+ return formatDiffResult(result, format);
846
+ }
847
+ const summaryRaw = await transport.command("git_diff_summary", { workspace, staged });
848
+ const summary = summaryRaw?.diffSummary ?? summaryRaw;
849
+ if (summary?.success === false || summary?.reason) {
850
+ const msg = summary?.error ?? summary?.reason ?? "unknown";
851
+ if (format === "json") return JSON.stringify({ error: msg }, null, 2);
852
+ return `Git diff error: ${msg}`;
853
+ }
854
+ if (!summary?.isGitRepo) {
855
+ const msg = `Not a git repository: ${workspace}`;
856
+ if (format === "json") return JSON.stringify({ error: msg }, null, 2);
857
+ return msg;
858
+ }
859
+ const files = summary?.files ?? [];
860
+ if (files.length === 0) {
861
+ if (format === "json") return JSON.stringify({ files: [], total_files: 0, shown_files: 0, truncated: false }, null, 2);
862
+ return "No changed files.";
863
+ }
864
+ const topFiles = files.slice(0, 5);
865
+ const fileDiffs = await Promise.all(
866
+ topFiles.map(async (f) => {
867
+ try {
868
+ const raw = await transport.command("git_diff_file", { workspace, path: f.path, staged });
869
+ const d = raw?.diff ?? raw;
870
+ const lines = (d?.diff ?? "").split("\n");
871
+ const trunc = lines.length > maxLines;
872
+ return {
873
+ path: f.path,
874
+ old_path: f.oldPath ?? null,
875
+ status: f.status ?? "M",
876
+ diff: trunc ? lines.slice(0, maxLines).join("\n") + "\n... (truncated)" : d?.diff ?? "",
877
+ truncated: trunc,
878
+ binary: d?.binary ?? false
879
+ };
880
+ } catch {
881
+ return { path: f.path, diff: "", truncated: false, binary: false, error: "fetch failed" };
882
+ }
883
+ })
884
+ );
885
+ return formatDiffResult({
886
+ files: fileDiffs,
887
+ total_files: files.length,
888
+ shown_files: topFiles.length,
889
+ truncated: files.length > 5
890
+ }, format);
891
+ }
892
+ function formatDiffResult(result, format) {
893
+ if (format === "json") return JSON.stringify(result, null, 2);
894
+ const files = result?.files ?? [];
895
+ if (files.length === 0) return "No changed files.";
896
+ const parts = [];
897
+ const totalShown = result?.shown_files ?? files.length;
898
+ const totalAll = result?.total_files ?? files.length;
899
+ if (totalAll > totalShown) {
900
+ parts.push(`Showing ${totalShown} of ${totalAll} changed files:
901
+ `);
902
+ }
903
+ for (const f of files) {
904
+ const header = `--- ${f.path}${f.old_path ? ` (was ${f.old_path})` : ""} ---`;
905
+ if (f.error) {
906
+ parts.push(`${header}
907
+ (error: ${f.error})
908
+ `);
909
+ } else if (f.binary) {
910
+ parts.push(`${header}
911
+ (binary file)
912
+ `);
913
+ } else if (!f.diff) {
914
+ parts.push(`${header}
915
+ (no diff)
916
+ `);
917
+ } else {
918
+ parts.push(`${header}
919
+ ${f.diff}${f.truncated ? "" : "\n"}`);
920
+ }
921
+ }
922
+ return parts.join("\n");
923
+ }
924
+
925
+ // src/tools/git-checkpoint.ts
926
+ var GIT_CHECKPOINT_TOOL = {
927
+ name: "git_checkpoint",
928
+ description: "Create a checkpoint commit in a workspace. Stages all tracked changes (or all files including untracked) and commits with a prefixed message. Use this to save progress before a risky operation, or to create a restore point the orchestrator can reference.",
929
+ inputSchema: {
930
+ type: "object",
931
+ properties: {
932
+ workspace: {
933
+ type: "string",
934
+ description: "Absolute path to the workspace/repository directory."
935
+ },
936
+ message: {
937
+ type: "string",
938
+ description: 'Checkpoint message (max 200 chars). Will be prefixed with "adhdev: checkpoint ".'
939
+ },
940
+ include_untracked: {
941
+ type: "boolean",
942
+ description: "Also stage and commit untracked files (default: false)."
943
+ },
944
+ daemon_id: {
945
+ type: "string",
946
+ description: "Daemon ID (cloud mode only, required)."
947
+ }
948
+ },
949
+ required: ["workspace", "message"]
950
+ }
951
+ };
952
+ async function gitCheckpoint(transport, args) {
953
+ const message = args.message?.trim();
954
+ if (!message) return "Error: message is required";
955
+ if (message.length > 200) return "Error: message must be 200 characters or fewer";
956
+ let raw;
957
+ if (isLocalTransport(transport)) {
958
+ raw = await transport.command("git_checkpoint", {
959
+ workspace: args.workspace,
960
+ message,
961
+ includeUntracked: args.include_untracked ?? false
962
+ });
963
+ raw = raw?.checkpoint ?? raw;
964
+ } else {
965
+ if (!args.daemon_id) throw new Error("daemon_id is required in cloud mode");
966
+ const result = await transport.gitCheckpoint(args.daemon_id, {
967
+ workspace: args.workspace,
968
+ message,
969
+ includeUntracked: args.include_untracked ?? false
970
+ });
971
+ raw = result?.checkpoint ?? result;
972
+ }
973
+ if (raw?.success === false || raw?.reason) {
974
+ const msg = raw?.error ?? raw?.reason ?? "unknown";
975
+ if (msg.includes("Nothing to commit") || msg.includes("nothing to commit")) {
976
+ return "Nothing to commit \u2014 working tree is clean.";
977
+ }
978
+ return `Git checkpoint error: ${msg}`;
979
+ }
980
+ const commit = raw?.commit?.slice(0, 7) ?? "???????";
981
+ const fullMsg = raw?.message ?? `adhdev: checkpoint ${message}`;
982
+ return `Checkpoint created: ${commit} \u2014 ${fullMsg}`;
983
+ }
984
+
985
+ // src/tools/git-push.ts
986
+ var GIT_PUSH_TOOL = {
987
+ name: "git_push",
988
+ description: "Push a branch to a remote repository on the daemon machine. If the branch has no upstream configured, sets it automatically. Key for parallel multi-machine workflows: after git_checkpoint, push each machine's branch to origin so changes are available for PR/review.",
989
+ inputSchema: {
990
+ type: "object",
991
+ properties: {
992
+ workspace: {
993
+ type: "string",
994
+ description: "Absolute path to the workspace/repository directory."
995
+ },
996
+ remote: {
997
+ type: "string",
998
+ description: 'Remote name (default: "origin").'
999
+ },
1000
+ branch: {
1001
+ type: "string",
1002
+ description: "Branch to push (default: current branch)."
1003
+ },
1004
+ daemon_id: {
1005
+ type: "string",
1006
+ description: "Daemon ID (cloud mode only, required)."
1007
+ }
1008
+ },
1009
+ required: ["workspace"]
1010
+ }
1011
+ };
1012
+ async function gitPush(transport, args) {
1013
+ let raw;
1014
+ if (isLocalTransport(transport)) {
1015
+ raw = await transport.command("git_push", {
1016
+ workspace: args.workspace,
1017
+ remote: args.remote ?? "origin",
1018
+ ...args.branch ? { branch: args.branch } : {}
1019
+ });
1020
+ raw = raw?.push ?? raw;
1021
+ } else {
1022
+ if (!args.daemon_id) throw new Error("daemon_id is required in cloud mode");
1023
+ const result = await transport.gitPush(args.daemon_id, {
1024
+ workspace: args.workspace,
1025
+ remote: args.remote,
1026
+ branch: args.branch
1027
+ });
1028
+ raw = result?.push ?? result;
1029
+ }
1030
+ if (raw?.success === false || raw?.reason) {
1031
+ const msg = raw?.error ?? raw?.reason ?? "unknown";
1032
+ return `Git push error: ${msg}`;
1033
+ }
1034
+ const branch = raw?.branch ?? args.branch ?? "(current)";
1035
+ const remote = raw?.remote ?? args.remote ?? "origin";
1036
+ const newBranch = raw?.newBranch ? " [new branch]" : "";
1037
+ const output = raw?.output ? `
1038
+ ${raw.output}` : "";
1039
+ return `Pushed ${branch} \u2192 ${remote}${newBranch}${output}`;
1040
+ }
1041
+
622
1042
  // src/tools/launch-session.ts
623
1043
  var LAUNCH_SESSION_TOOL = {
624
1044
  name: "launch_session",
@@ -647,7 +1067,7 @@ var LAUNCH_SESSION_TOOL = {
647
1067
  }
648
1068
  };
649
1069
  async function launchSession(transport, args) {
650
- if ("command" in transport) {
1070
+ if (isLocalTransport(transport)) {
651
1071
  const isCliOrAcp = args.type.includes("-cli") || args.type.includes("-acp") || args.type === "codex";
652
1072
  const commandType = isCliOrAcp ? "launch_cli" : "launch_ide";
653
1073
  const payload = isCliOrAcp ? { cliType: args.type, dir: args.workspace ?? "~", ...args.model ? { model: args.model } : {} } : { ideType: args.type, enableCdp: true };
@@ -684,14 +1104,14 @@ var STOP_SESSION_TOOL = {
684
1104
  },
685
1105
  type: {
686
1106
  type: "string",
687
- description: "Provider type (e.g. hermes-cli, claude-cli). Cloud mode: auto-resolved from session_id if omitted."
1107
+ description: "Provider type (e.g. hermes-cli, claude-cli). Local mode auto-resolves from session_id if omitted; cloud mode forwards the session_id and omits type unless explicitly provided."
688
1108
  }
689
1109
  },
690
1110
  required: ["session_id"]
691
1111
  }
692
1112
  };
693
1113
  async function stopSession(transport, args) {
694
- if ("command" in transport) {
1114
+ if (isLocalTransport(transport)) {
695
1115
  const local = transport;
696
1116
  let resolvedType = args.type;
697
1117
  if (!resolvedType) {
@@ -735,7 +1155,7 @@ var CHECK_PENDING_TOOL = {
735
1155
  }
736
1156
  };
737
1157
  async function checkPending(transport, args) {
738
- if ("getStatus" in transport) {
1158
+ if (isLocalTransport(transport)) {
739
1159
  return checkPendingLocal(transport, args.format);
740
1160
  }
741
1161
  return checkPendingCloud(transport, args.daemon_id, args.format);
@@ -843,10 +1263,14 @@ async function startMcpServer(opts) {
843
1263
  SEND_CHAT_TOOL,
844
1264
  APPROVE_TOOL,
845
1265
  GIT_STATUS_TOOL,
1266
+ GIT_LOG_TOOL,
1267
+ GIT_DIFF_TOOL,
1268
+ GIT_CHECKPOINT_TOOL,
1269
+ GIT_PUSH_TOOL,
846
1270
  ...isLocal ? [SCREENSHOT_TOOL] : []
847
1271
  ];
848
1272
  const server = new import_server.Server(
849
- { name: "adhdev-mcp-server", version: "0.9.62" },
1273
+ { name: "adhdev-mcp-server", version: "0.9.64" },
850
1274
  { capabilities: { tools: {} } }
851
1275
  );
852
1276
  server.setRequestHandler(import_types.ListToolsRequestSchema, async () => ({ tools: allTools }));
@@ -889,6 +1313,22 @@ async function startMcpServer(opts) {
889
1313
  const text = await gitStatus(transport, { workspace: a.workspace, include_diff: a.include_diff, daemon_id: a.daemon_id, format: a.format });
890
1314
  return { content: [{ type: "text", text }] };
891
1315
  }
1316
+ case "git_log": {
1317
+ const text = await gitLog(transport, { workspace: a.workspace, limit: a.limit, file: a.file, since: a.since, until: a.until, daemon_id: a.daemon_id, format: a.format });
1318
+ return { content: [{ type: "text", text }] };
1319
+ }
1320
+ case "git_diff": {
1321
+ const text = await gitDiff(transport, { workspace: a.workspace, file: a.file, max_lines: a.max_lines, staged: a.staged, daemon_id: a.daemon_id, format: a.format });
1322
+ return { content: [{ type: "text", text }] };
1323
+ }
1324
+ case "git_checkpoint": {
1325
+ const text = await gitCheckpoint(transport, { workspace: a.workspace, message: a.message, include_untracked: a.include_untracked, daemon_id: a.daemon_id });
1326
+ return { content: [{ type: "text", text }] };
1327
+ }
1328
+ case "git_push": {
1329
+ const text = await gitPush(transport, { workspace: a.workspace, remote: a.remote, branch: a.branch, daemon_id: a.daemon_id });
1330
+ return { content: [{ type: "text", text }] };
1331
+ }
892
1332
  case "launch_session": {
893
1333
  const text = await launchSession(transport, {
894
1334
  type: a.type,
@@ -976,8 +1416,8 @@ Environment variables:
976
1416
  ADHDEV_API_KEY API key (cloud mode)
977
1417
  ADHDEV_PASSWORD Daemon password (local mode)
978
1418
 
979
- Local mode tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status, screenshot
980
- Cloud mode tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status
1419
+ Local mode tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status, git_log, git_diff, git_checkpoint, git_push, screenshot
1420
+ Cloud mode tools: list_daemons, list_sessions, launch_session, stop_session, check_pending, read_chat, send_chat, approve, git_status, git_log, git_diff, git_checkpoint, git_push
981
1421
  `.trim());
982
1422
  }
983
1423
  startMcpServer(parseArgs(process.argv)).catch((err) => {