codebase-cli 2.0.0-pre.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.
Files changed (172) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/bin/codebase +2 -0
  4. package/dist/agent/agent.js +198 -0
  5. package/dist/agent/agent.js.map +1 -0
  6. package/dist/agent/config.js +117 -0
  7. package/dist/agent/config.js.map +1 -0
  8. package/dist/agent/events.js +153 -0
  9. package/dist/agent/events.js.map +1 -0
  10. package/dist/agent/router.js +35 -0
  11. package/dist/agent/router.js.map +1 -0
  12. package/dist/agent/system-prompt.js +21 -0
  13. package/dist/agent/system-prompt.js.map +1 -0
  14. package/dist/auth/cli.js +138 -0
  15. package/dist/auth/cli.js.map +1 -0
  16. package/dist/auth/credentials.js +105 -0
  17. package/dist/auth/credentials.js.map +1 -0
  18. package/dist/auth/flow.js +222 -0
  19. package/dist/auth/flow.js.map +1 -0
  20. package/dist/auth/pkce.js +46 -0
  21. package/dist/auth/pkce.js.map +1 -0
  22. package/dist/cli.js +69 -0
  23. package/dist/cli.js.map +1 -0
  24. package/dist/clipboard/copy.js +106 -0
  25. package/dist/clipboard/copy.js.map +1 -0
  26. package/dist/commands/builtins.js +203 -0
  27. package/dist/commands/builtins.js.map +1 -0
  28. package/dist/commands/registry.js +65 -0
  29. package/dist/commands/registry.js.map +1 -0
  30. package/dist/commands/types.js +2 -0
  31. package/dist/commands/types.js.map +1 -0
  32. package/dist/compaction/engine.js +209 -0
  33. package/dist/compaction/engine.js.map +1 -0
  34. package/dist/compaction/tokens.js +79 -0
  35. package/dist/compaction/tokens.js.map +1 -0
  36. package/dist/compaction/types.js +2 -0
  37. package/dist/compaction/types.js.map +1 -0
  38. package/dist/diagnostics/checkers.js +211 -0
  39. package/dist/diagnostics/checkers.js.map +1 -0
  40. package/dist/diagnostics/engine.js +71 -0
  41. package/dist/diagnostics/engine.js.map +1 -0
  42. package/dist/diagnostics/types.js +2 -0
  43. package/dist/diagnostics/types.js.map +1 -0
  44. package/dist/dotenv/loader.js +115 -0
  45. package/dist/dotenv/loader.js.map +1 -0
  46. package/dist/glue/client.js +47 -0
  47. package/dist/glue/client.js.map +1 -0
  48. package/dist/glue/intent.js +78 -0
  49. package/dist/glue/intent.js.map +1 -0
  50. package/dist/glue/narration.js +55 -0
  51. package/dist/glue/narration.js.map +1 -0
  52. package/dist/headless/run.js +89 -0
  53. package/dist/headless/run.js.map +1 -0
  54. package/dist/hooks/manager.js +158 -0
  55. package/dist/hooks/manager.js.map +1 -0
  56. package/dist/hooks/runner.js +70 -0
  57. package/dist/hooks/runner.js.map +1 -0
  58. package/dist/hooks/types.js +2 -0
  59. package/dist/hooks/types.js.map +1 -0
  60. package/dist/memory/inject.js +12 -0
  61. package/dist/memory/inject.js.map +1 -0
  62. package/dist/memory/store.js +178 -0
  63. package/dist/memory/store.js.map +1 -0
  64. package/dist/memory/types.js +10 -0
  65. package/dist/memory/types.js.map +1 -0
  66. package/dist/permissions/store.js +172 -0
  67. package/dist/permissions/store.js.map +1 -0
  68. package/dist/plan/flow.js +214 -0
  69. package/dist/plan/flow.js.map +1 -0
  70. package/dist/plan/prompts.js +69 -0
  71. package/dist/plan/prompts.js.map +1 -0
  72. package/dist/plan/store.js +37 -0
  73. package/dist/plan/store.js.map +1 -0
  74. package/dist/plan/types.js +3 -0
  75. package/dist/plan/types.js.map +1 -0
  76. package/dist/sessions/store.js +105 -0
  77. package/dist/sessions/store.js.map +1 -0
  78. package/dist/skills/loader.js +41 -0
  79. package/dist/skills/loader.js.map +1 -0
  80. package/dist/skills/platform-loader.js +63 -0
  81. package/dist/skills/platform-loader.js.map +1 -0
  82. package/dist/skills/types.js +21 -0
  83. package/dist/skills/types.js.map +1 -0
  84. package/dist/tools/ask-user.js +61 -0
  85. package/dist/tools/ask-user.js.map +1 -0
  86. package/dist/tools/dispatch-agent.js +178 -0
  87. package/dist/tools/dispatch-agent.js.map +1 -0
  88. package/dist/tools/edit-file.js +80 -0
  89. package/dist/tools/edit-file.js.map +1 -0
  90. package/dist/tools/errors.js +89 -0
  91. package/dist/tools/errors.js.map +1 -0
  92. package/dist/tools/file-ops.js +136 -0
  93. package/dist/tools/file-ops.js.map +1 -0
  94. package/dist/tools/file-state-cache.js +92 -0
  95. package/dist/tools/file-state-cache.js.map +1 -0
  96. package/dist/tools/git/branch.js +84 -0
  97. package/dist/tools/git/branch.js.map +1 -0
  98. package/dist/tools/git/commit.js +83 -0
  99. package/dist/tools/git/commit.js.map +1 -0
  100. package/dist/tools/git/diff.js +72 -0
  101. package/dist/tools/git/diff.js.map +1 -0
  102. package/dist/tools/git/git-helpers.js +58 -0
  103. package/dist/tools/git/git-helpers.js.map +1 -0
  104. package/dist/tools/git/log.js +70 -0
  105. package/dist/tools/git/log.js.map +1 -0
  106. package/dist/tools/git/status.js +97 -0
  107. package/dist/tools/git/status.js.map +1 -0
  108. package/dist/tools/git/worktree.js +132 -0
  109. package/dist/tools/git/worktree.js.map +1 -0
  110. package/dist/tools/glob.js +128 -0
  111. package/dist/tools/glob.js.map +1 -0
  112. package/dist/tools/grep.js +199 -0
  113. package/dist/tools/grep.js.map +1 -0
  114. package/dist/tools/list-files.js +120 -0
  115. package/dist/tools/list-files.js.map +1 -0
  116. package/dist/tools/memory-tools.js +127 -0
  117. package/dist/tools/memory-tools.js.map +1 -0
  118. package/dist/tools/multi-edit.js +87 -0
  119. package/dist/tools/multi-edit.js.map +1 -0
  120. package/dist/tools/notebook-edit.js +147 -0
  121. package/dist/tools/notebook-edit.js.map +1 -0
  122. package/dist/tools/permission.js +168 -0
  123. package/dist/tools/permission.js.map +1 -0
  124. package/dist/tools/plan-mode.js +76 -0
  125. package/dist/tools/plan-mode.js.map +1 -0
  126. package/dist/tools/read-file.js +135 -0
  127. package/dist/tools/read-file.js.map +1 -0
  128. package/dist/tools/registry.js +52 -0
  129. package/dist/tools/registry.js.map +1 -0
  130. package/dist/tools/shell.js +216 -0
  131. package/dist/tools/shell.js.map +1 -0
  132. package/dist/tools/task-store.js +70 -0
  133. package/dist/tools/task-store.js.map +1 -0
  134. package/dist/tools/tasks.js +131 -0
  135. package/dist/tools/tasks.js.map +1 -0
  136. package/dist/tools/types.js +2 -0
  137. package/dist/tools/types.js.map +1 -0
  138. package/dist/tools/web-fetch.js +152 -0
  139. package/dist/tools/web-fetch.js.map +1 -0
  140. package/dist/tools/web-search.js +169 -0
  141. package/dist/tools/web-search.js.map +1 -0
  142. package/dist/tools/write-file.js +70 -0
  143. package/dist/tools/write-file.js.map +1 -0
  144. package/dist/types.js +9 -0
  145. package/dist/types.js.map +1 -0
  146. package/dist/ui/App.js +216 -0
  147. package/dist/ui/App.js.map +1 -0
  148. package/dist/ui/Input.js +90 -0
  149. package/dist/ui/Input.js.map +1 -0
  150. package/dist/ui/Message.js +89 -0
  151. package/dist/ui/Message.js.map +1 -0
  152. package/dist/ui/MessageList.js +35 -0
  153. package/dist/ui/MessageList.js.map +1 -0
  154. package/dist/ui/Permission.js +39 -0
  155. package/dist/ui/Permission.js.map +1 -0
  156. package/dist/ui/Status.js +34 -0
  157. package/dist/ui/Status.js.map +1 -0
  158. package/dist/ui/TaskPanel.js +43 -0
  159. package/dist/ui/TaskPanel.js.map +1 -0
  160. package/dist/ui/Throbber.js +20 -0
  161. package/dist/ui/Throbber.js.map +1 -0
  162. package/dist/ui/ToolPanel.js +83 -0
  163. package/dist/ui/ToolPanel.js.map +1 -0
  164. package/dist/ui/UserQuery.js +38 -0
  165. package/dist/ui/UserQuery.js.map +1 -0
  166. package/dist/ui/input-state.js +210 -0
  167. package/dist/ui/input-state.js.map +1 -0
  168. package/dist/ui/wrap.js +30 -0
  169. package/dist/ui/wrap.js.map +1 -0
  170. package/dist/user-queries/store.js +60 -0
  171. package/dist/user-queries/store.js.map +1 -0
  172. package/package.json +76 -0
@@ -0,0 +1,136 @@
1
+ import { randomBytes } from "node:crypto";
2
+ import { existsSync, mkdirSync, renameSync, statSync, unlinkSync, writeFileSync } from "node:fs";
3
+ import { dirname, isAbsolute, resolve, sep } from "node:path";
4
+ import { AmbiguousMatchError, FileNotReadFirstError, FileUnexpectedlyModifiedError, NoMatchError, PartialViewEditError, PathOutsideCwdError, } from "./errors.js";
5
+ /**
6
+ * Resolve a user-supplied path against cwd and reject anything that escapes.
7
+ * Symlinks are followed at read/stat time, so this is a best-effort guard
8
+ * against absolute paths and `..` traversal — combine with realpath checks
9
+ * before sensitive operations if you don't trust the working tree.
10
+ */
11
+ export function resolveInsideCwd(cwd, requested) {
12
+ const absCwd = resolve(cwd);
13
+ const candidate = isAbsolute(requested) ? resolve(requested) : resolve(absCwd, requested);
14
+ if (candidate !== absCwd && !candidate.startsWith(absCwd + sep)) {
15
+ throw new PathOutsideCwdError(requested);
16
+ }
17
+ return candidate;
18
+ }
19
+ /** Detect newline style. Empty string for files with no newline. */
20
+ export function detectEol(content) {
21
+ const idx = content.indexOf("\n");
22
+ if (idx === -1)
23
+ return "";
24
+ return idx > 0 && content[idx - 1] === "\r" ? "\r\n" : "\n";
25
+ }
26
+ /** Strip a UTF-8 BOM if present and return the decoded string. */
27
+ export function stripBOM(buf) {
28
+ if (buf.length >= 3 && buf[0] === 0xef && buf[1] === 0xbb && buf[2] === 0xbf) {
29
+ return { content: buf.subarray(3).toString("utf8"), hasBOM: true };
30
+ }
31
+ return { content: buf.toString("utf8"), hasBOM: false };
32
+ }
33
+ /** Heuristic null-byte scan over the first 8 KB. */
34
+ export function isLikelyBinary(buf) {
35
+ const slice = buf.subarray(0, Math.min(buf.length, 8192));
36
+ for (let i = 0; i < slice.length; i++) {
37
+ if (slice[i] === 0)
38
+ return true;
39
+ }
40
+ return false;
41
+ }
42
+ /**
43
+ * Validate a file is safe to overwrite. Throws typed errors so the LLM gets
44
+ * actionable feedback when its context has drifted from disk:
45
+ * - missing snapshot → FileNotReadFirstError ("read it first")
46
+ * - mtime/size drift → FileUnexpectedlyModifiedError ("read it again")
47
+ * - partial-view read → PartialViewEditError ("read the full file")
48
+ */
49
+ export function validateForOverwrite(absPath, cache) {
50
+ const status = cache.check(absPath);
51
+ if (status === "missing")
52
+ throw new FileNotReadFirstError(absPath);
53
+ if (status === "modified")
54
+ throw new FileUnexpectedlyModifiedError(absPath);
55
+ const snap = cache.get(absPath);
56
+ if (!snap)
57
+ throw new FileNotReadFirstError(absPath);
58
+ if (snap.isPartialView)
59
+ throw new PartialViewEditError(absPath);
60
+ return snap;
61
+ }
62
+ /**
63
+ * Apply a single string replacement. Single-match by default; replace_all
64
+ * substitutes every occurrence. Throws NoMatchError / AmbiguousMatchError
65
+ * with file-path context so the model can self-correct.
66
+ */
67
+ export function applyEdit(content, opts) {
68
+ const { oldString, newString, replaceAll, path } = opts;
69
+ if (oldString.length === 0) {
70
+ throw new Error("old_string must be non-empty. Use write_file to create a new file.");
71
+ }
72
+ if (oldString === newString) {
73
+ throw new Error("old_string and new_string are identical; nothing to change.");
74
+ }
75
+ if (replaceAll) {
76
+ const parts = content.split(oldString);
77
+ if (parts.length === 1)
78
+ throw new NoMatchError(path);
79
+ return { content: parts.join(newString), replacements: parts.length - 1 };
80
+ }
81
+ const first = content.indexOf(oldString);
82
+ if (first === -1)
83
+ throw new NoMatchError(path);
84
+ const second = content.indexOf(oldString, first + oldString.length);
85
+ if (second !== -1) {
86
+ let count = 2;
87
+ let cursor = second + oldString.length;
88
+ while (true) {
89
+ const next = content.indexOf(oldString, cursor);
90
+ if (next === -1)
91
+ break;
92
+ count++;
93
+ cursor = next + oldString.length;
94
+ }
95
+ throw new AmbiguousMatchError(path, count);
96
+ }
97
+ return {
98
+ content: content.slice(0, first) + newString + content.slice(first + oldString.length),
99
+ replacements: 1,
100
+ };
101
+ }
102
+ /**
103
+ * Write content to disk via tmp+rename. Restores BOM and line endings from
104
+ * the originating snapshot so a Windows-authored file stays Windows-formatted
105
+ * after edits. The tmp file is removed on failure.
106
+ */
107
+ export function atomicWrite(absPath, content, options = {}) {
108
+ const eol = options.eol ?? detectEol(content);
109
+ const normalized = eol === "\r\n" ? content.replace(/\r?\n/g, "\r\n") : eol === "\n" ? content.replace(/\r\n/g, "\n") : content;
110
+ let buf = Buffer.from(normalized, "utf8");
111
+ if (options.hasBOM) {
112
+ buf = Buffer.concat([Buffer.from([0xef, 0xbb, 0xbf]), buf]);
113
+ }
114
+ mkdirSync(dirname(absPath), { recursive: true });
115
+ const tmp = `${absPath}.${randomBytes(4).toString("hex")}.tmp`;
116
+ try {
117
+ writeFileSync(tmp, buf, { mode: options.mode ?? 0o644 });
118
+ renameSync(tmp, absPath);
119
+ }
120
+ catch (err) {
121
+ try {
122
+ unlinkSync(tmp);
123
+ }
124
+ catch {
125
+ // best-effort cleanup
126
+ }
127
+ throw err;
128
+ }
129
+ const stat = statSync(absPath);
130
+ return { mtimeMs: stat.mtimeMs, size: stat.size };
131
+ }
132
+ /** True if the path exists on disk (without throwing). */
133
+ export function pathExists(absPath) {
134
+ return existsSync(absPath);
135
+ }
136
+ //# sourceMappingURL=file-ops.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-ops.js","sourceRoot":"","sources":["../../src/tools/file-ops.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import { statSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ /**
4
+ * LRU cache of read file states. Map iteration order in JS is insertion
5
+ * order, which we abuse for LRU: every `get`/`record` re-inserts to push
6
+ * the entry to the tail; eviction takes from the head.
7
+ */
8
+ export class FileStateCache {
9
+ snapshots = new Map();
10
+ bytesStored = 0;
11
+ maxEntries;
12
+ byteBudget;
13
+ constructor(config = {}) {
14
+ this.maxEntries = config.maxEntries ?? 100;
15
+ this.byteBudget = config.byteBudget ?? 25_000_000;
16
+ }
17
+ record(snapshot) {
18
+ const key = resolve(snapshot.path);
19
+ const existing = this.snapshots.get(key);
20
+ if (existing) {
21
+ this.bytesStored -= byteSize(existing.content);
22
+ this.snapshots.delete(key);
23
+ }
24
+ const stored = { ...snapshot, path: key };
25
+ this.snapshots.set(key, stored);
26
+ this.bytesStored += byteSize(snapshot.content);
27
+ this.evictIfNeeded();
28
+ }
29
+ get(path) {
30
+ const key = resolve(path);
31
+ const snapshot = this.snapshots.get(key);
32
+ if (snapshot) {
33
+ this.snapshots.delete(key);
34
+ this.snapshots.set(key, snapshot);
35
+ }
36
+ return snapshot;
37
+ }
38
+ invalidate(path) {
39
+ const key = resolve(path);
40
+ const existing = this.snapshots.get(key);
41
+ if (existing) {
42
+ this.bytesStored -= byteSize(existing.content);
43
+ this.snapshots.delete(key);
44
+ }
45
+ }
46
+ /**
47
+ * Compare cached state to disk:
48
+ * - "fresh": cache hit and mtime matches; edit can proceed
49
+ * - "modified": cache hit but mtime drifted (or file gone); caller must re-read
50
+ * - "missing": no cached snapshot; caller must read first
51
+ */
52
+ check(path) {
53
+ const snapshot = this.get(path);
54
+ if (!snapshot)
55
+ return "missing";
56
+ try {
57
+ const stat = statSync(snapshot.path);
58
+ if (stat.mtimeMs !== snapshot.mtimeMs || stat.size !== snapshot.size) {
59
+ return "modified";
60
+ }
61
+ return "fresh";
62
+ }
63
+ catch {
64
+ return "modified";
65
+ }
66
+ }
67
+ size() {
68
+ return this.snapshots.size;
69
+ }
70
+ bytes() {
71
+ return this.bytesStored;
72
+ }
73
+ clear() {
74
+ this.snapshots.clear();
75
+ this.bytesStored = 0;
76
+ }
77
+ evictIfNeeded() {
78
+ while ((this.snapshots.size > this.maxEntries || this.bytesStored > this.byteBudget) && this.snapshots.size > 0) {
79
+ const oldestKey = this.snapshots.keys().next().value;
80
+ if (!oldestKey)
81
+ return;
82
+ const snap = this.snapshots.get(oldestKey);
83
+ if (snap)
84
+ this.bytesStored -= byteSize(snap.content);
85
+ this.snapshots.delete(oldestKey);
86
+ }
87
+ }
88
+ }
89
+ function byteSize(s) {
90
+ return Buffer.byteLength(s, "utf8");
91
+ }
92
+ //# sourceMappingURL=file-state-cache.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-state-cache.js","sourceRoot":"","sources":["../../src/tools/file-state-cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,84 @@
1
+ import { Type } from "typebox";
2
+ import { requireGitRepo, runGit } from "./git-helpers.js";
3
+ const Params = Type.Object({
4
+ name: Type.Optional(Type.String({
5
+ description: "Branch name. Omit to list branches.",
6
+ })),
7
+ create: Type.Optional(Type.Boolean({
8
+ description: "Create the branch (errors if it already exists). Requires name.",
9
+ })),
10
+ base: Type.Optional(Type.String({
11
+ description: "Base ref for new branches. Defaults to HEAD. Only used with create.",
12
+ })),
13
+ });
14
+ const DESCRIPTION = `Manage git branches.
15
+
16
+ Modes (selected by which fields you set):
17
+ - No name → list local + remote-tracking branches.
18
+ - name only → switch to that branch (\`git switch\`). Permission-gated.
19
+ - name + create: true → create a new branch (off base or HEAD) and switch to it. Permission-gated.
20
+
21
+ Listing is read-only and skips the permission prompt. Creating or switching mutates working-tree state and prompts.`;
22
+ export function createGitBranch(ctx) {
23
+ return {
24
+ name: "git_branch",
25
+ label: "Git branch",
26
+ description: DESCRIPTION,
27
+ parameters: Params,
28
+ executionMode: "sequential",
29
+ execute: async (_id, params, signal) => {
30
+ await requireGitRepo(ctx.cwd);
31
+ if (!params.name) {
32
+ const r = await runGit(["branch", "-a", "--no-color"], ctx.cwd, signal);
33
+ if (r.exitCode !== 0) {
34
+ throw new Error(r.stderr.trim() || `git branch exited ${r.exitCode}`);
35
+ }
36
+ const { branches, current } = parseBranchList(r.stdout);
37
+ return {
38
+ content: [{ type: "text", text: branches.length ? branches.join("\n") : "(no branches)" }],
39
+ details: { mode: "list", branches, current: current ?? undefined },
40
+ };
41
+ }
42
+ if (params.create) {
43
+ const args = ["switch", "-c", params.name];
44
+ if (params.base)
45
+ args.push(params.base);
46
+ const r = await runGit(args, ctx.cwd, signal);
47
+ if (r.exitCode !== 0) {
48
+ throw new Error(r.stderr.trim() || `git switch -c ${params.name} exited ${r.exitCode}`);
49
+ }
50
+ return {
51
+ content: [{ type: "text", text: `created and switched to ${params.name}` }],
52
+ details: { mode: "create", branch: params.name },
53
+ };
54
+ }
55
+ const r = await runGit(["switch", params.name], ctx.cwd, signal);
56
+ if (r.exitCode !== 0) {
57
+ throw new Error(r.stderr.trim() || `git switch ${params.name} exited ${r.exitCode}`);
58
+ }
59
+ return {
60
+ content: [{ type: "text", text: `switched to ${params.name}` }],
61
+ details: { mode: "switch", branch: params.name },
62
+ };
63
+ },
64
+ };
65
+ }
66
+ function parseBranchList(output) {
67
+ const branches = [];
68
+ let current = null;
69
+ for (const raw of output.split("\n")) {
70
+ const line = raw.trim();
71
+ if (!line)
72
+ continue;
73
+ if (line.startsWith("* ")) {
74
+ const name = line.slice(2).trim();
75
+ current = name;
76
+ branches.push(`* ${name}`);
77
+ }
78
+ else {
79
+ branches.push(` ${line.replace(/^[+-]\s*/, "")}`);
80
+ }
81
+ }
82
+ return { branches, current };
83
+ }
84
+ //# sourceMappingURL=branch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branch.js","sourceRoot":"","sources":["../../../src/tools/git/branch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ import { Type } from "typebox";
2
+ import { requireGitRepo, runGit } from "./git-helpers.js";
3
+ const Params = Type.Object({
4
+ message: Type.String({
5
+ minLength: 1,
6
+ description: "Commit message. Multi-line messages are written via -F to preserve formatting.",
7
+ }),
8
+ files: Type.Optional(Type.Array(Type.String(), {
9
+ description: "Specific paths to stage before commit. Mutually exclusive with stage_all.",
10
+ })),
11
+ stage_all: Type.Optional(Type.Boolean({
12
+ description: "Run `git add -A` before commit. Mutually exclusive with files.",
13
+ })),
14
+ });
15
+ const DESCRIPTION = `Create a git commit. Permission-gated.
16
+
17
+ Behavior:
18
+ - If files is set, runs \`git add <paths>\` first, then commits only those.
19
+ - If stage_all is set, runs \`git add -A\` first.
20
+ - If neither is set, commits whatever's currently staged.
21
+ - Multi-line messages are passed via -F (a temp file) to preserve formatting.
22
+ - Pre-commit hooks run normally; their failure stderr surfaces to you.
23
+ - Errors with "nothing to commit" if the index is empty after staging.`;
24
+ export function createGitCommit(ctx) {
25
+ return {
26
+ name: "git_commit",
27
+ label: "Git commit",
28
+ description: DESCRIPTION,
29
+ parameters: Params,
30
+ executionMode: "sequential",
31
+ execute: async (_id, params, signal) => {
32
+ await requireGitRepo(ctx.cwd);
33
+ if (params.files && params.stage_all) {
34
+ throw new Error("Pass files or stage_all, not both.");
35
+ }
36
+ if (params.stage_all) {
37
+ const r = await runGit(["add", "-A"], ctx.cwd, signal);
38
+ if (r.exitCode !== 0) {
39
+ throw new Error(r.stderr.trim() || `git add -A exited ${r.exitCode}`);
40
+ }
41
+ }
42
+ else if (params.files && params.files.length > 0) {
43
+ const r = await runGit(["add", "--", ...params.files], ctx.cwd, signal);
44
+ if (r.exitCode !== 0) {
45
+ throw new Error(r.stderr.trim() || `git add exited ${r.exitCode}`);
46
+ }
47
+ }
48
+ const commit = await runGit(["commit", "-F", "-"], ctx.cwd, signal, params.message);
49
+ if (commit.exitCode !== 0) {
50
+ const stderr = commit.stderr.trim();
51
+ const stdout = commit.stdout.trim();
52
+ const reason = stderr || stdout || `git commit exited ${commit.exitCode}`;
53
+ throw new Error(reason);
54
+ }
55
+ const subject = params.message.split("\n")[0] ?? "";
56
+ const sha = await readHeadSha(ctx.cwd, signal);
57
+ const branch = await readCurrentBranch(ctx.cwd, signal);
58
+ return {
59
+ content: [
60
+ {
61
+ type: "text",
62
+ text: sha && branch ? `committed ${sha.slice(0, 7)} on ${branch}: ${subject}` : `committed: ${subject}`,
63
+ },
64
+ ],
65
+ details: { sha, subject, branch },
66
+ };
67
+ },
68
+ };
69
+ }
70
+ async function readHeadSha(cwd, signal) {
71
+ const r = await runGit(["rev-parse", "HEAD"], cwd, signal);
72
+ if (r.exitCode !== 0)
73
+ return null;
74
+ return r.stdout.trim() || null;
75
+ }
76
+ async function readCurrentBranch(cwd, signal) {
77
+ const r = await runGit(["rev-parse", "--abbrev-ref", "HEAD"], cwd, signal);
78
+ if (r.exitCode !== 0)
79
+ return null;
80
+ const branch = r.stdout.trim();
81
+ return branch && branch !== "HEAD" ? branch : null;
82
+ }
83
+ //# sourceMappingURL=commit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"commit.js","sourceRoot":"","sources":["../../../src/tools/git/commit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,72 @@
1
+ import { Type } from "typebox";
2
+ import { requireGitRepo, runGit } from "./git-helpers.js";
3
+ const Params = Type.Object({
4
+ staged: Type.Optional(Type.Boolean({
5
+ description: "Show staged changes (--cached). Default false (working-tree changes).",
6
+ })),
7
+ ref: Type.Optional(Type.String({
8
+ description: "Compare against a specific ref (commit, branch, tag). Mutually exclusive with staged.",
9
+ })),
10
+ path: Type.Optional(Type.String({
11
+ description: "Limit the diff to a path (file or directory).",
12
+ })),
13
+ max_lines: Type.Optional(Type.Integer({
14
+ minimum: 1,
15
+ maximum: 10000,
16
+ description: "Cap on diff output lines. Default 2000.",
17
+ })),
18
+ });
19
+ const DEFAULT_LINE_LIMIT = 2000;
20
+ const DESCRIPTION = `Show a unified diff. Defaults to working-tree vs index. Set staged: true to see staged-vs-HEAD, or ref: <commit/branch/tag> to compare HEAD against that ref. Limit to a path with the path argument.
21
+
22
+ Output is the raw diff text, capped at 2000 lines by default. Use git_log to find a ref to diff against.`;
23
+ export function createGitDiff(ctx) {
24
+ return {
25
+ name: "git_diff",
26
+ label: "Git diff",
27
+ description: DESCRIPTION,
28
+ parameters: Params,
29
+ executionMode: "parallel",
30
+ execute: async (_id, params, signal) => {
31
+ await requireGitRepo(ctx.cwd);
32
+ if (params.staged && params.ref) {
33
+ throw new Error("Pass either staged or ref, not both.");
34
+ }
35
+ const argv = ["diff", "--no-color"];
36
+ let mode = "working";
37
+ if (params.staged) {
38
+ argv.push("--cached");
39
+ mode = "staged";
40
+ }
41
+ else if (params.ref) {
42
+ argv.push(params.ref);
43
+ mode = "ref";
44
+ }
45
+ if (params.path) {
46
+ argv.push("--", params.path);
47
+ }
48
+ const r = await runGit(argv, ctx.cwd, signal);
49
+ if (r.exitCode !== 0 && r.exitCode !== 1) {
50
+ // 1 means "differences found", which is normal for diff
51
+ throw new Error(r.stderr.trim() || `git diff exited ${r.exitCode}`);
52
+ }
53
+ const limit = params.max_lines ?? DEFAULT_LINE_LIMIT;
54
+ const allLines = r.stdout.split("\n");
55
+ const truncated = allLines.length > limit;
56
+ const shown = truncated ? allLines.slice(0, limit) : allLines;
57
+ const tail = truncated ? `\n... (showing ${limit} of ${allLines.length} lines)` : "";
58
+ const text = shown.join("\n") + tail;
59
+ return {
60
+ content: [{ type: "text", text: text || "(no changes)" }],
61
+ details: {
62
+ mode,
63
+ ref: params.ref ?? null,
64
+ path: params.path ?? null,
65
+ bytes: r.stdout.length,
66
+ truncated,
67
+ },
68
+ };
69
+ },
70
+ };
71
+ }
72
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../../../src/tools/git/diff.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { spawn } from "node:child_process";
2
+ /**
3
+ * Spawn `git` with the given args. Returns a Promise that resolves with
4
+ * captured stdout, stderr, and exit code. Never throws — caller decides
5
+ * whether non-zero is an error in their context.
6
+ *
7
+ * If `stdin` is provided it's written to the child's stdin and stdin is
8
+ * closed (used for `git commit -F -` to feed multi-line messages without
9
+ * a temp file).
10
+ */
11
+ export function runGit(args, cwd, signal, stdin) {
12
+ return new Promise((resolveRun) => {
13
+ const child = spawn("git", args, {
14
+ cwd,
15
+ env: process.env,
16
+ stdio: [stdin === undefined ? "ignore" : "pipe", "pipe", "pipe"],
17
+ });
18
+ if (stdin !== undefined) {
19
+ try {
20
+ child.stdin?.write(stdin);
21
+ child.stdin?.end();
22
+ }
23
+ catch {
24
+ // stdin already closed by a fast exit — that's fine
25
+ }
26
+ }
27
+ const out = [];
28
+ const err = [];
29
+ child.stdout?.on("data", (b) => out.push(b));
30
+ child.stderr?.on("data", (b) => err.push(b));
31
+ const onAbort = () => child.kill("SIGTERM");
32
+ signal?.addEventListener("abort", onAbort);
33
+ child.on("error", (e) => {
34
+ signal?.removeEventListener("abort", onAbort);
35
+ resolveRun({ stdout: "", stderr: e.message, exitCode: 1 });
36
+ });
37
+ child.on("close", (code) => {
38
+ signal?.removeEventListener("abort", onAbort);
39
+ resolveRun({
40
+ stdout: Buffer.concat(out).toString("utf8"),
41
+ stderr: Buffer.concat(err).toString("utf8"),
42
+ exitCode: code ?? 1,
43
+ });
44
+ });
45
+ });
46
+ }
47
+ /** True if cwd is inside a git work tree. */
48
+ export async function isGitRepo(cwd) {
49
+ const r = await runGit(["rev-parse", "--is-inside-work-tree"], cwd);
50
+ return r.exitCode === 0 && r.stdout.trim() === "true";
51
+ }
52
+ /** Throws an actionable error if cwd is not a git repo. */
53
+ export async function requireGitRepo(cwd) {
54
+ if (!(await isGitRepo(cwd))) {
55
+ throw new Error(`Not a git repository (or any parent): ${cwd}`);
56
+ }
57
+ }
58
+ //# sourceMappingURL=git-helpers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git-helpers.js","sourceRoot":"","sources":["../../../src/tools/git/git-helpers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,70 @@
1
+ import { Type } from "typebox";
2
+ import { requireGitRepo, runGit } from "./git-helpers.js";
3
+ const Params = Type.Object({
4
+ count: Type.Optional(Type.Integer({
5
+ minimum: 1,
6
+ maximum: 200,
7
+ description: "Number of commits to show. Default 10, max 200.",
8
+ })),
9
+ oneline: Type.Optional(Type.Boolean({
10
+ description: "Compact one-line-per-commit output (--oneline). Default true.",
11
+ })),
12
+ path: Type.Optional(Type.String({
13
+ description: "Limit history to commits touching this path.",
14
+ })),
15
+ ref: Type.Optional(Type.String({
16
+ description: "Show history of a specific ref (branch, tag). Defaults to HEAD.",
17
+ })),
18
+ });
19
+ const DEFAULT_COUNT = 10;
20
+ const DESCRIPTION = `Show recent commits. Defaults to 10 most recent on HEAD, in --oneline format (short SHA + subject).
21
+
22
+ Pass count to fetch more, ref to log a specific branch/tag, or path to filter to commits that touched a file or directory.`;
23
+ export function createGitLog(ctx) {
24
+ return {
25
+ name: "git_log",
26
+ label: "Git log",
27
+ description: DESCRIPTION,
28
+ parameters: Params,
29
+ executionMode: "parallel",
30
+ execute: async (_id, params, signal) => {
31
+ await requireGitRepo(ctx.cwd);
32
+ const count = params.count ?? DEFAULT_COUNT;
33
+ const oneline = params.oneline ?? true;
34
+ const argv = ["log", `-n${count}`, "--no-color"];
35
+ if (oneline)
36
+ argv.push("--oneline");
37
+ if (params.ref)
38
+ argv.push(params.ref);
39
+ if (params.path)
40
+ argv.push("--", params.path);
41
+ const r = await runGit(argv, ctx.cwd, signal);
42
+ if (r.exitCode !== 0) {
43
+ throw new Error(r.stderr.trim() || `git log exited ${r.exitCode}`);
44
+ }
45
+ const entries = oneline ? parseOneline(r.stdout) : [];
46
+ return {
47
+ content: [{ type: "text", text: r.stdout.trim() || "(no commits)" }],
48
+ details: {
49
+ count: oneline ? entries.length : r.stdout.split("\ncommit ").length,
50
+ entries,
51
+ ref: params.ref ?? null,
52
+ path: params.path ?? null,
53
+ },
54
+ };
55
+ },
56
+ };
57
+ }
58
+ function parseOneline(stdout) {
59
+ const entries = [];
60
+ for (const line of stdout.split("\n")) {
61
+ if (!line)
62
+ continue;
63
+ const space = line.indexOf(" ");
64
+ if (space === -1)
65
+ continue;
66
+ entries.push({ sha: line.slice(0, space), subject: line.slice(space + 1) });
67
+ }
68
+ return entries;
69
+ }
70
+ //# sourceMappingURL=log.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"log.js","sourceRoot":"","sources":["../../../src/tools/git/log.ts"],"names":[],"mappings":"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"}