overarch-linux-arm64 0.8.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 (127) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +214 -0
  3. package/dist/bin/overarch.d.ts +2 -0
  4. package/dist/bin/overarch.js +178 -0
  5. package/dist/src/agent/context.d.ts +14 -0
  6. package/dist/src/agent/context.js +200 -0
  7. package/dist/src/agent/context_compressor.d.ts +21 -0
  8. package/dist/src/agent/context_compressor.js +90 -0
  9. package/dist/src/agent/manifest.d.ts +22 -0
  10. package/dist/src/agent/manifest.js +131 -0
  11. package/dist/src/agent/planner.d.ts +38 -0
  12. package/dist/src/agent/planner.js +118 -0
  13. package/dist/src/agent/runtime.d.ts +59 -0
  14. package/dist/src/agent/runtime.js +400 -0
  15. package/dist/src/cli/approval.d.ts +13 -0
  16. package/dist/src/cli/approval.js +71 -0
  17. package/dist/src/cli/args.d.ts +15 -0
  18. package/dist/src/cli/args.js +47 -0
  19. package/dist/src/cli/commands/audit.d.ts +10 -0
  20. package/dist/src/cli/commands/audit.js +79 -0
  21. package/dist/src/cli/commands/companion.d.ts +1 -0
  22. package/dist/src/cli/commands/companion.js +143 -0
  23. package/dist/src/cli/commands/config.d.ts +1 -0
  24. package/dist/src/cli/commands/config.js +135 -0
  25. package/dist/src/cli/commands/doctor.d.ts +1 -0
  26. package/dist/src/cli/commands/doctor.js +217 -0
  27. package/dist/src/cli/commands/init.d.ts +1 -0
  28. package/dist/src/cli/commands/init.js +134 -0
  29. package/dist/src/cli/commands/memory.d.ts +2 -0
  30. package/dist/src/cli/commands/memory.js +98 -0
  31. package/dist/src/cli/commands/onboarding.d.ts +3 -0
  32. package/dist/src/cli/commands/onboarding.js +223 -0
  33. package/dist/src/cli/commands/sessions.d.ts +1 -0
  34. package/dist/src/cli/commands/sessions.js +22 -0
  35. package/dist/src/cli/commands/status.d.ts +1 -0
  36. package/dist/src/cli/commands/status.js +37 -0
  37. package/dist/src/cli/commands/update.d.ts +9 -0
  38. package/dist/src/cli/commands/update.js +105 -0
  39. package/dist/src/cli/repl.d.ts +3 -0
  40. package/dist/src/cli/repl.js +821 -0
  41. package/dist/src/cli/ui.d.ts +53 -0
  42. package/dist/src/cli/ui.js +215 -0
  43. package/dist/src/companion/petdex_adapter.d.ts +31 -0
  44. package/dist/src/companion/petdex_adapter.js +171 -0
  45. package/dist/src/companion/petdex_bridge.d.ts +65 -0
  46. package/dist/src/companion/petdex_bridge.js +356 -0
  47. package/dist/src/companion/types.d.ts +57 -0
  48. package/dist/src/companion/types.js +11 -0
  49. package/dist/src/config/index.d.ts +24 -0
  50. package/dist/src/config/index.js +237 -0
  51. package/dist/src/config/types.d.ts +58 -0
  52. package/dist/src/config/types.js +7 -0
  53. package/dist/src/core/diagnostics.d.ts +48 -0
  54. package/dist/src/core/diagnostics.js +115 -0
  55. package/dist/src/core/index.d.ts +23 -0
  56. package/dist/src/core/index.js +23 -0
  57. package/dist/src/core/mission.d.ts +55 -0
  58. package/dist/src/core/mission.js +173 -0
  59. package/dist/src/index.d.ts +1 -0
  60. package/dist/src/index.js +1 -0
  61. package/dist/src/learning/memory_manager.d.ts +26 -0
  62. package/dist/src/learning/memory_manager.js +211 -0
  63. package/dist/src/learning/types.d.ts +54 -0
  64. package/dist/src/learning/types.js +1 -0
  65. package/dist/src/mcp/registry.d.ts +17 -0
  66. package/dist/src/mcp/registry.js +97 -0
  67. package/dist/src/mcp/types.d.ts +13 -0
  68. package/dist/src/mcp/types.js +1 -0
  69. package/dist/src/providers/anthropic.d.ts +10 -0
  70. package/dist/src/providers/anthropic.js +95 -0
  71. package/dist/src/providers/base.d.ts +15 -0
  72. package/dist/src/providers/base.js +108 -0
  73. package/dist/src/providers/factory.d.ts +28 -0
  74. package/dist/src/providers/factory.js +142 -0
  75. package/dist/src/providers/gemini.d.ts +10 -0
  76. package/dist/src/providers/gemini.js +97 -0
  77. package/dist/src/providers/openai.d.ts +9 -0
  78. package/dist/src/providers/openai.js +85 -0
  79. package/dist/src/providers/openrouter.d.ts +5 -0
  80. package/dist/src/providers/openrouter.js +14 -0
  81. package/dist/src/providers/registry.d.ts +19 -0
  82. package/dist/src/providers/registry.js +209 -0
  83. package/dist/src/providers/types.d.ts +59 -0
  84. package/dist/src/providers/types.js +1 -0
  85. package/dist/src/providers/validator.d.ts +24 -0
  86. package/dist/src/providers/validator.js +161 -0
  87. package/dist/src/security/audit.d.ts +56 -0
  88. package/dist/src/security/audit.js +154 -0
  89. package/dist/src/security/guardrails.d.ts +9 -0
  90. package/dist/src/security/guardrails.js +32 -0
  91. package/dist/src/security/permissions.d.ts +12 -0
  92. package/dist/src/security/permissions.js +68 -0
  93. package/dist/src/security/policy.d.ts +220 -0
  94. package/dist/src/security/policy.js +720 -0
  95. package/dist/src/session/manager.d.ts +22 -0
  96. package/dist/src/session/manager.js +172 -0
  97. package/dist/src/session/types.d.ts +36 -0
  98. package/dist/src/session/types.js +1 -0
  99. package/dist/src/tools/checkpoint.d.ts +26 -0
  100. package/dist/src/tools/checkpoint.js +160 -0
  101. package/dist/src/tools/diagnostics.d.ts +2 -0
  102. package/dist/src/tools/diagnostics.js +125 -0
  103. package/dist/src/tools/diff.d.ts +14 -0
  104. package/dist/src/tools/diff.js +124 -0
  105. package/dist/src/tools/filesystem.d.ts +2 -0
  106. package/dist/src/tools/filesystem.js +224 -0
  107. package/dist/src/tools/git.d.ts +2 -0
  108. package/dist/src/tools/git.js +138 -0
  109. package/dist/src/tools/markdown.d.ts +33 -0
  110. package/dist/src/tools/markdown.js +190 -0
  111. package/dist/src/tools/project.d.ts +2 -0
  112. package/dist/src/tools/project.js +63 -0
  113. package/dist/src/tools/registry.d.ts +14 -0
  114. package/dist/src/tools/registry.js +230 -0
  115. package/dist/src/tools/search.d.ts +2 -0
  116. package/dist/src/tools/search.js +136 -0
  117. package/dist/src/tools/shell.d.ts +2 -0
  118. package/dist/src/tools/shell.js +74 -0
  119. package/dist/src/tools/symbols.d.ts +30 -0
  120. package/dist/src/tools/symbols.js +155 -0
  121. package/dist/src/tools/types.d.ts +28 -0
  122. package/dist/src/tools/types.js +1 -0
  123. package/dist/src/tools/web_search.d.ts +14 -0
  124. package/dist/src/tools/web_search.js +154 -0
  125. package/install.sh +29 -0
  126. package/overarch +15 -0
  127. package/package.json +28 -0
@@ -0,0 +1,124 @@
1
+ /**
2
+ * Lightweight, zero-dependency line-by-line unified diff generator.
3
+ * Follows the Ponytail principle: efficient, standard library only, edge-case correct.
4
+ */
5
+ /**
6
+ * Computes line-by-line diff between two text strings using standard LCS algorithm.
7
+ */
8
+ export function computeLineDiff(oldText, newText, contextLines = 3) {
9
+ // Normalize line endings
10
+ const oldLines = oldText === '' ? [] : oldText.replace(/\r\n/g, '\n').split('\n');
11
+ const newLines = newText === '' ? [] : newText.replace(/\r\n/g, '\n').split('\n');
12
+ if (oldText === newText) {
13
+ return { additions: 0, deletions: 0, unifiedDiff: '', hasChanges: false };
14
+ }
15
+ // LCS matrix for line changes (bounded to prevent huge memory usage)
16
+ const maxLines = 1500;
17
+ if (oldLines.length > maxLines || newLines.length > maxLines) {
18
+ // Fast path for very large files: line count comparison
19
+ const additions = Math.max(0, newLines.length - oldLines.length);
20
+ const deletions = Math.max(0, oldLines.length - newLines.length);
21
+ return {
22
+ additions,
23
+ deletions,
24
+ unifiedDiff: `@@ Large file (${oldLines.length} -> ${newLines.length} lines) @@`,
25
+ hasChanges: true
26
+ };
27
+ }
28
+ const m = oldLines.length;
29
+ const n = newLines.length;
30
+ const dp = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));
31
+ for (let i = 0; i < m; i++) {
32
+ for (let j = 0; j < n; j++) {
33
+ if (oldLines[i] === newLines[j]) {
34
+ dp[i + 1][j + 1] = dp[i][j] + 1;
35
+ }
36
+ else {
37
+ dp[i + 1][j + 1] = Math.max(dp[i + 1][j], dp[i][j + 1]);
38
+ }
39
+ }
40
+ }
41
+ const edits = [];
42
+ let i = m;
43
+ let j = n;
44
+ while (i > 0 || j > 0) {
45
+ if (i > 0 && j > 0 && oldLines[i - 1] === newLines[j - 1]) {
46
+ edits.unshift({ type: 'same', line: oldLines[i - 1], oldNum: i, newNum: j });
47
+ i--;
48
+ j--;
49
+ }
50
+ else if (j > 0 && (i === 0 || dp[i][j - 1] >= dp[i - 1][j])) {
51
+ edits.unshift({ type: 'add', line: newLines[j - 1], newNum: j });
52
+ j--;
53
+ }
54
+ else if (i > 0) {
55
+ edits.unshift({ type: 'del', line: oldLines[i - 1], oldNum: i });
56
+ i--;
57
+ }
58
+ }
59
+ let additions = 0;
60
+ let deletions = 0;
61
+ const diffLines = [];
62
+ // Group into hunks with context
63
+ let inHunk = false;
64
+ let hunkBuffer = [];
65
+ for (let k = 0; k < edits.length; k++) {
66
+ const edit = edits[k];
67
+ if (edit.type === 'add')
68
+ additions++;
69
+ if (edit.type === 'del')
70
+ deletions++;
71
+ if (edit.type !== 'same') {
72
+ if (!inHunk) {
73
+ // Collect preceding context
74
+ inHunk = true;
75
+ const start = Math.max(0, k - contextLines);
76
+ hunkBuffer = edits.slice(start, k);
77
+ }
78
+ hunkBuffer.push(edit);
79
+ }
80
+ else if (inHunk) {
81
+ hunkBuffer.push(edit);
82
+ // Check if subsequent lines have changes within context window
83
+ const hasUpcomingChange = edits.slice(k + 1, k + 1 + contextLines * 2).some(e => e.type !== 'same');
84
+ if (!hasUpcomingChange) {
85
+ // Flush hunk
86
+ const trimmedHunk = hunkBuffer.slice(0, hunkBuffer.length);
87
+ const firstEdit = trimmedHunk[0];
88
+ const oldStart = firstEdit.oldNum || 1;
89
+ const newStart = firstEdit.newNum || 1;
90
+ diffLines.push(`@@ -${oldStart} +${newStart} @@`);
91
+ for (const h of trimmedHunk) {
92
+ if (h.type === 'same')
93
+ diffLines.push(` ${h.line}`);
94
+ else if (h.type === 'add')
95
+ diffLines.push(`+ ${h.line}`);
96
+ else if (h.type === 'del')
97
+ diffLines.push(`- ${h.line}`);
98
+ }
99
+ hunkBuffer = [];
100
+ inHunk = false;
101
+ }
102
+ }
103
+ }
104
+ if (inHunk && hunkBuffer.length > 0) {
105
+ const firstEdit = hunkBuffer[0];
106
+ const oldStart = firstEdit.oldNum || 1;
107
+ const newStart = firstEdit.newNum || 1;
108
+ diffLines.push(`@@ -${oldStart} +${newStart} @@`);
109
+ for (const h of hunkBuffer) {
110
+ if (h.type === 'same')
111
+ diffLines.push(` ${h.line}`);
112
+ else if (h.type === 'add')
113
+ diffLines.push(`+ ${h.line}`);
114
+ else if (h.type === 'del')
115
+ diffLines.push(`- ${h.line}`);
116
+ }
117
+ }
118
+ return {
119
+ additions,
120
+ deletions,
121
+ unifiedDiff: diffLines.join('\n'),
122
+ hasChanges: additions > 0 || deletions > 0
123
+ };
124
+ }
@@ -0,0 +1,2 @@
1
+ import { Tool } from './types.js';
2
+ export declare function createFilesystemTools(): Tool[];
@@ -0,0 +1,224 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { PermissionTier } from '../config/types.js';
4
+ import { computeLineDiff } from './diff.js';
5
+ export function createFilesystemTools() {
6
+ return [
7
+ {
8
+ definition: {
9
+ name: 'readFile',
10
+ description: 'Read contents of a file, optionally with line range',
11
+ tier: PermissionTier.ReadOnly,
12
+ parameters: [
13
+ { name: 'path', type: 'string', description: 'File path', required: true },
14
+ { name: 'startLine', type: 'number', description: '1-indexed starting line (optional)', required: false },
15
+ { name: 'endLine', type: 'number', description: '1-indexed ending line (optional)', required: false }
16
+ ]
17
+ },
18
+ handler: async (params) => {
19
+ const filePath = path.resolve(process.cwd(), String(params.path));
20
+ try {
21
+ const stats = await fs.stat(filePath);
22
+ const content = await fs.readFile(filePath, 'utf8');
23
+ const startLine = params.startLine ? Math.max(1, Number(params.startLine)) : undefined;
24
+ const endLine = params.endLine ? Math.max(1, Number(params.endLine)) : undefined;
25
+ if (startLine !== undefined || endLine !== undefined) {
26
+ const lines = content.replace(/\r\n/g, '\n').split('\n');
27
+ const start = (startLine ? startLine - 1 : 0);
28
+ const end = (endLine ? Math.min(lines.length, endLine) : lines.length);
29
+ const sliced = lines.slice(start, end);
30
+ const numbered = sliced.map((line, idx) => `${start + idx + 1} | ${line}`).join('\n');
31
+ return {
32
+ success: true,
33
+ output: numbered,
34
+ metadata: { totalLines: lines.length, startLine: start + 1, endLine: end }
35
+ };
36
+ }
37
+ if (stats.size > 100 * 1024) {
38
+ const handle = await fs.open(filePath, 'r');
39
+ const buffer = Buffer.alloc(100 * 1024);
40
+ await handle.read(buffer, 0, 100 * 1024, 0);
41
+ await handle.close();
42
+ return {
43
+ success: true,
44
+ output: buffer.toString('utf8') + '\n... [TRUNCATED - use startLine/endLine for specific sections]',
45
+ };
46
+ }
47
+ return { success: true, output: content };
48
+ }
49
+ catch (e) {
50
+ return { success: false, output: '', error: e.message };
51
+ }
52
+ }
53
+ },
54
+ {
55
+ definition: {
56
+ name: 'writeFile',
57
+ description: 'Write content to a file. Generates diff if overwriting existing file.',
58
+ tier: PermissionTier.StateMutating,
59
+ parameters: [
60
+ { name: 'path', type: 'string', description: 'File path', required: true },
61
+ { name: 'content', type: 'string', description: 'File content', required: true }
62
+ ]
63
+ },
64
+ handler: async (params) => {
65
+ const filePath = path.resolve(process.cwd(), String(params.path));
66
+ const newContent = String(params.content);
67
+ // Checkpoint before mutating file
68
+ const checkpointMgr = new (await import('../tools/checkpoint.js')).CheckpointManager();
69
+ await checkpointMgr.create(`writeFile:${params.path}`, [filePath]);
70
+ try {
71
+ let existingContent = '';
72
+ let fileExisted = false;
73
+ try {
74
+ existingContent = await fs.readFile(filePath, 'utf8');
75
+ fileExisted = true;
76
+ }
77
+ catch { }
78
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
79
+ await fs.writeFile(filePath, newContent, 'utf8');
80
+ const diffResult = computeLineDiff(existingContent, newContent);
81
+ const relPath = path.relative(process.cwd(), filePath);
82
+ return {
83
+ success: true,
84
+ output: fileExisted ? `Updated ${relPath}` : `Created ${relPath}`,
85
+ metadata: {
86
+ path: relPath,
87
+ existed: fileExisted,
88
+ additions: diffResult.additions,
89
+ deletions: diffResult.deletions,
90
+ diff: diffResult.unifiedDiff
91
+ }
92
+ };
93
+ }
94
+ catch (e) {
95
+ return { success: false, output: '', error: e.message };
96
+ }
97
+ }
98
+ },
99
+ {
100
+ definition: {
101
+ name: 'editFile',
102
+ description: 'Replace search text with replace text in a file with diff tracking',
103
+ tier: PermissionTier.StateMutating,
104
+ parameters: [
105
+ { name: 'path', type: 'string', description: 'File path', required: true },
106
+ { name: 'search', type: 'string', description: 'Exact string to search for', required: true },
107
+ { name: 'replace', type: 'string', description: 'Exact string to replace with', required: true }
108
+ ]
109
+ },
110
+ handler: async (params) => {
111
+ const filePath = path.resolve(process.cwd(), String(params.path));
112
+ try {
113
+ const content = await fs.readFile(filePath, 'utf8');
114
+ const searchStr = String(params.search);
115
+ const replaceStr = String(params.replace);
116
+ let targetFound = content.includes(searchStr);
117
+ let actualSearch = searchStr;
118
+ // If exact match not found, check with normalized line endings
119
+ if (!targetFound) {
120
+ const normalizedContent = content.replace(/\r\n/g, '\n');
121
+ const normalizedSearch = searchStr.replace(/\r\n/g, '\n');
122
+ if (normalizedContent.includes(normalizedSearch)) {
123
+ actualSearch = normalizedSearch;
124
+ targetFound = true;
125
+ }
126
+ }
127
+ if (!targetFound) {
128
+ return {
129
+ success: false,
130
+ output: '',
131
+ error: `Search string not found in ${params.path}. Use readFile to verify the exact content.`
132
+ };
133
+ }
134
+ const newContent = content.replace(actualSearch, replaceStr);
135
+ await fs.writeFile(filePath, newContent, 'utf8');
136
+ const diffResult = computeLineDiff(content, newContent);
137
+ const relPath = path.relative(process.cwd(), filePath);
138
+ return {
139
+ success: true,
140
+ output: `Replaced in ${relPath}`,
141
+ metadata: {
142
+ path: relPath,
143
+ additions: diffResult.additions,
144
+ deletions: diffResult.deletions,
145
+ diff: diffResult.unifiedDiff
146
+ }
147
+ };
148
+ }
149
+ catch (e) {
150
+ return { success: false, output: '', error: e.message };
151
+ }
152
+ }
153
+ },
154
+ {
155
+ definition: {
156
+ name: 'listDirectory',
157
+ description: 'List contents of a directory with file sizes and types',
158
+ tier: PermissionTier.ReadOnly,
159
+ parameters: [
160
+ { name: 'path', type: 'string', description: 'Directory path', required: true },
161
+ { name: 'recursive', type: 'boolean', description: 'Recursive listing', required: false }
162
+ ]
163
+ },
164
+ handler: async (params) => {
165
+ const dirPath = path.resolve(process.cwd(), String(params.path));
166
+ const recursive = !!params.recursive;
167
+ const results = [];
168
+ let count = 0;
169
+ const ignore = new Set(['node_modules', '.git', 'dist', 'build', '.turbo', '.next']);
170
+ async function walk(currentPath, depth) {
171
+ if (count >= 200 || depth > 3)
172
+ return;
173
+ try {
174
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
175
+ for (const entry of entries) {
176
+ if (count >= 200)
177
+ break;
178
+ if (ignore.has(entry.name))
179
+ continue;
180
+ const fullPath = path.join(currentPath, entry.name);
181
+ const relPath = path.relative(dirPath, fullPath);
182
+ if (entry.isDirectory()) {
183
+ results.push(`DIR ${relPath}`);
184
+ count++;
185
+ if (recursive)
186
+ await walk(fullPath, depth + 1);
187
+ }
188
+ else if (entry.isFile()) {
189
+ const stats = await fs.stat(fullPath);
190
+ results.push(`FILE ${relPath} (${stats.size} bytes)`);
191
+ count++;
192
+ }
193
+ }
194
+ }
195
+ catch (e) {
196
+ // ignore access errors
197
+ }
198
+ }
199
+ await walk(dirPath, 1);
200
+ return { success: true, output: results.join('\n') || 'Empty directory' };
201
+ }
202
+ },
203
+ {
204
+ definition: {
205
+ name: 'createDirectory',
206
+ description: 'Create directory recursively',
207
+ tier: PermissionTier.SafeMutation,
208
+ parameters: [
209
+ { name: 'path', type: 'string', description: 'Directory path', required: true }
210
+ ]
211
+ },
212
+ handler: async (params) => {
213
+ const dirPath = path.resolve(process.cwd(), String(params.path));
214
+ try {
215
+ await fs.mkdir(dirPath, { recursive: true });
216
+ return { success: true, output: `Directory created: ${params.path}` };
217
+ }
218
+ catch (e) {
219
+ return { success: false, output: '', error: e.message };
220
+ }
221
+ }
222
+ }
223
+ ];
224
+ }
@@ -0,0 +1,2 @@
1
+ import { Tool } from './types.js';
2
+ export declare function createGitTools(): Tool[];
@@ -0,0 +1,138 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import { PermissionTier } from '../config/types.js';
4
+ const execFileAsync = promisify(execFile);
5
+ export function createGitTools() {
6
+ return [
7
+ {
8
+ definition: {
9
+ name: 'gitStatus',
10
+ description: 'Get git branch, modified files, and staging status',
11
+ tier: PermissionTier.ReadOnly,
12
+ parameters: []
13
+ },
14
+ handler: async () => {
15
+ try {
16
+ const { stdout: branch } = await execFileAsync('git', ['branch', '--show-current']);
17
+ const { stdout: status } = await execFileAsync('git', ['status', '--porcelain']);
18
+ return {
19
+ success: true,
20
+ output: `Branch: ${branch.trim()}\n\nStatus:\n${status || 'Clean working tree'}`
21
+ };
22
+ }
23
+ catch (e) {
24
+ return { success: false, output: '', error: e.message };
25
+ }
26
+ }
27
+ },
28
+ {
29
+ definition: {
30
+ name: 'gitDiff',
31
+ description: 'Get git diff for working tree or staged changes',
32
+ tier: PermissionTier.ReadOnly,
33
+ parameters: [
34
+ { name: 'staged', type: 'boolean', description: 'Diff staged files', required: false },
35
+ { name: 'path', type: 'string', description: 'Specific path to diff', required: false }
36
+ ]
37
+ },
38
+ handler: async (params) => {
39
+ try {
40
+ const args = ['diff'];
41
+ if (params.staged)
42
+ args.push('--staged');
43
+ if (params.path)
44
+ args.push('--', String(params.path));
45
+ const { stdout } = await execFileAsync('git', args);
46
+ return { success: true, output: stdout.slice(0, 20480) || 'No diff' };
47
+ }
48
+ catch (e) {
49
+ return { success: false, output: '', error: e.message };
50
+ }
51
+ }
52
+ },
53
+ {
54
+ definition: {
55
+ name: 'gitLog',
56
+ description: 'Get git commit log history',
57
+ tier: PermissionTier.ReadOnly,
58
+ parameters: [
59
+ { name: 'count', type: 'number', description: 'Number of commits to show', required: false }
60
+ ]
61
+ },
62
+ handler: async (params) => {
63
+ try {
64
+ const count = params.count ? Number(params.count) : 10;
65
+ const { stdout } = await execFileAsync('git', ['log', '--oneline', `-n`, String(count)]);
66
+ return { success: true, output: stdout || 'No commits yet.' };
67
+ }
68
+ catch (e) {
69
+ return { success: false, output: '', error: e.message };
70
+ }
71
+ }
72
+ },
73
+ {
74
+ definition: {
75
+ name: 'gitBranch',
76
+ description: 'List branches or create a new git branch',
77
+ tier: PermissionTier.SafeMutation,
78
+ parameters: [
79
+ { name: 'create', type: 'string', description: 'New branch name to create and checkout', required: false }
80
+ ]
81
+ },
82
+ handler: async (params) => {
83
+ try {
84
+ if (params.create) {
85
+ const branchName = String(params.create).trim();
86
+ const { stdout } = await execFileAsync('git', ['checkout', '-b', branchName]);
87
+ return { success: true, output: stdout || `Created and switched to branch ${branchName}` };
88
+ }
89
+ const { stdout } = await execFileAsync('git', ['branch', '-a']);
90
+ return { success: true, output: stdout };
91
+ }
92
+ catch (e) {
93
+ return { success: false, output: '', error: e.message };
94
+ }
95
+ }
96
+ },
97
+ {
98
+ definition: {
99
+ name: 'gitStage',
100
+ description: 'Stage files for commit (git add)',
101
+ tier: PermissionTier.SafeMutation,
102
+ parameters: [
103
+ { name: 'path', type: 'string', description: 'File path to stage, or "." for all', required: false }
104
+ ]
105
+ },
106
+ handler: async (params) => {
107
+ try {
108
+ const target = params.path ? String(params.path) : '.';
109
+ await execFileAsync('git', ['add', target]);
110
+ return { success: true, output: `Staged: ${target}` };
111
+ }
112
+ catch (e) {
113
+ return { success: false, output: '', error: e.message };
114
+ }
115
+ }
116
+ },
117
+ {
118
+ definition: {
119
+ name: 'gitCommit',
120
+ description: 'Commit staged changes with a descriptive message',
121
+ tier: PermissionTier.StateMutating,
122
+ parameters: [
123
+ { name: 'message', type: 'string', description: 'Commit message', required: true }
124
+ ]
125
+ },
126
+ handler: async (params) => {
127
+ try {
128
+ const msg = String(params.message);
129
+ const { stdout } = await execFileAsync('git', ['commit', '-m', msg]);
130
+ return { success: true, output: stdout };
131
+ }
132
+ catch (e) {
133
+ return { success: false, output: '', error: e.message };
134
+ }
135
+ }
136
+ }
137
+ ];
138
+ }
@@ -0,0 +1,33 @@
1
+ import { ToolResult } from './types.js';
2
+ export interface MarkdownHeading {
3
+ level: number;
4
+ text: string;
5
+ line: number;
6
+ }
7
+ export interface MarkdownStructure {
8
+ frontMatter?: Record<string, string>;
9
+ headings: MarkdownHeading[];
10
+ codeBlocksCount: number;
11
+ tablesCount: number;
12
+ rawContent: string;
13
+ }
14
+ /**
15
+ * Parses markdown front matter (YAML-like key: value between --- fences)
16
+ */
17
+ export declare function parseFrontMatter(content: string): {
18
+ frontMatter?: Record<string, string>;
19
+ body: string;
20
+ };
21
+ /**
22
+ * Parses markdown structural elements (headings, code blocks, tables)
23
+ */
24
+ export declare function inspectMarkdown(content: string): MarkdownStructure;
25
+ /**
26
+ * Non-destructive markdown section patcher.
27
+ * Replaces ONLY the target section under a specific heading without corrupting surrounding text.
28
+ */
29
+ export declare function patchMarkdownSection(filePath: string, targetHeading: string, newSectionContent: string): Promise<ToolResult>;
30
+ /**
31
+ * Terminal syntax renderer for markdown text
32
+ */
33
+ export declare function renderMarkdown(content: string): string;