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,190 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { styleText } from 'node:util';
3
+ /**
4
+ * Parses markdown front matter (YAML-like key: value between --- fences)
5
+ */
6
+ export function parseFrontMatter(content) {
7
+ if (!content.startsWith('---')) {
8
+ return { body: content };
9
+ }
10
+ const endIdx = content.indexOf('\n---', 3);
11
+ if (endIdx === -1) {
12
+ return { body: content };
13
+ }
14
+ const fmLines = content.slice(3, endIdx).trim().split('\n');
15
+ const frontMatter = {};
16
+ for (const line of fmLines) {
17
+ const colon = line.indexOf(':');
18
+ if (colon > 0) {
19
+ const key = line.slice(0, colon).trim();
20
+ const val = line.slice(colon + 1).trim();
21
+ frontMatter[key] = val;
22
+ }
23
+ }
24
+ const body = content.slice(endIdx + 4).trimStart();
25
+ return { frontMatter, body };
26
+ }
27
+ /**
28
+ * Parses markdown structural elements (headings, code blocks, tables)
29
+ */
30
+ export function inspectMarkdown(content) {
31
+ const { frontMatter } = parseFrontMatter(content);
32
+ const lines = content.split('\n');
33
+ const headings = [];
34
+ let inCodeBlock = false;
35
+ let codeBlocksCount = 0;
36
+ let tablesCount = 0;
37
+ let inTable = false;
38
+ for (let i = 0; i < lines.length; i++) {
39
+ const line = lines[i];
40
+ if (line.trim().startsWith('```')) {
41
+ inCodeBlock = !inCodeBlock;
42
+ if (inCodeBlock)
43
+ codeBlocksCount++;
44
+ continue;
45
+ }
46
+ if (!inCodeBlock) {
47
+ const headingMatch = line.match(/^(#{1,6})\s+(.*)$/);
48
+ if (headingMatch) {
49
+ headings.push({
50
+ level: headingMatch[1].length,
51
+ text: headingMatch[2].trim(),
52
+ line: i + 1
53
+ });
54
+ }
55
+ else if (line.trim().startsWith('|') && line.trim().endsWith('|')) {
56
+ if (!inTable) {
57
+ tablesCount++;
58
+ inTable = true;
59
+ }
60
+ }
61
+ else {
62
+ inTable = false;
63
+ }
64
+ }
65
+ }
66
+ return {
67
+ frontMatter,
68
+ headings,
69
+ codeBlocksCount,
70
+ tablesCount,
71
+ rawContent: content
72
+ };
73
+ }
74
+ /**
75
+ * Non-destructive markdown section patcher.
76
+ * Replaces ONLY the target section under a specific heading without corrupting surrounding text.
77
+ */
78
+ export async function patchMarkdownSection(filePath, targetHeading, newSectionContent) {
79
+ let content;
80
+ try {
81
+ content = await fs.readFile(filePath, 'utf8');
82
+ }
83
+ catch (err) {
84
+ return { success: false, output: '', error: `Cannot read markdown file: ${err.message}` };
85
+ }
86
+ const lines = content.split('\n');
87
+ const targetClean = targetHeading.replace(/^#+\s*/, '').trim().toLowerCase();
88
+ let targetIndex = -1;
89
+ let targetLevel = 1;
90
+ let inCodeBlock = false;
91
+ for (let i = 0; i < lines.length; i++) {
92
+ const line = lines[i];
93
+ if (line.trim().startsWith('```')) {
94
+ inCodeBlock = !inCodeBlock;
95
+ continue;
96
+ }
97
+ if (!inCodeBlock) {
98
+ const match = line.match(/^(#{1,6})\s+(.*)$/);
99
+ if (match) {
100
+ const text = match[2].trim().toLowerCase();
101
+ if (text === targetClean) {
102
+ targetIndex = i;
103
+ targetLevel = match[1].length;
104
+ break;
105
+ }
106
+ }
107
+ }
108
+ }
109
+ if (targetIndex === -1) {
110
+ return {
111
+ success: false,
112
+ output: '',
113
+ error: `Heading '${targetHeading}' not found in ${filePath}`
114
+ };
115
+ }
116
+ // Find where this section ends (next heading of <= targetLevel, or EOF)
117
+ let nextSectionIndex = lines.length;
118
+ inCodeBlock = false;
119
+ for (let i = targetIndex + 1; i < lines.length; i++) {
120
+ const line = lines[i];
121
+ if (line.trim().startsWith('```')) {
122
+ inCodeBlock = !inCodeBlock;
123
+ continue;
124
+ }
125
+ if (!inCodeBlock) {
126
+ const match = line.match(/^(#{1,6})\s+(.*)$/);
127
+ if (match && match[1].length <= targetLevel) {
128
+ nextSectionIndex = i;
129
+ break;
130
+ }
131
+ }
132
+ }
133
+ // Preserve the heading line itself, replace lines between targetIndex + 1 and nextSectionIndex
134
+ const headingLine = lines[targetIndex];
135
+ const before = lines.slice(0, targetIndex + 1);
136
+ const after = lines.slice(nextSectionIndex);
137
+ const cleanNewContent = newSectionContent.trim();
138
+ const assembled = [...before, cleanNewContent, '', ...after].join('\n');
139
+ try {
140
+ await fs.writeFile(filePath, assembled, 'utf8');
141
+ return {
142
+ success: true,
143
+ output: `Successfully updated section '${headingLine}' in ${filePath}`,
144
+ metadata: { targetHeading, modifiedLines: nextSectionIndex - targetIndex }
145
+ };
146
+ }
147
+ catch (err) {
148
+ return { success: false, output: '', error: `Failed to write file: ${err.message}` };
149
+ }
150
+ }
151
+ /**
152
+ * Terminal syntax renderer for markdown text
153
+ */
154
+ export function renderMarkdown(content) {
155
+ const lines = content.split('\n');
156
+ const rendered = [];
157
+ let inCodeBlock = false;
158
+ for (const line of lines) {
159
+ if (line.trim().startsWith('```')) {
160
+ inCodeBlock = !inCodeBlock;
161
+ rendered.push(styleText('dim', line));
162
+ continue;
163
+ }
164
+ if (inCodeBlock) {
165
+ rendered.push(styleText('cyan', ` ${line}`));
166
+ continue;
167
+ }
168
+ const h1 = line.match(/^#\s+(.*)/);
169
+ if (h1) {
170
+ rendered.push(`\n${styleText(['bold', 'underline'], h1[1])}\n`);
171
+ continue;
172
+ }
173
+ const h2 = line.match(/^##\s+(.*)/);
174
+ if (h2) {
175
+ rendered.push(`\n${styleText(['bold', 'cyan'], h2[1])}`);
176
+ continue;
177
+ }
178
+ const h3 = line.match(/^###\s+(.*)/);
179
+ if (h3) {
180
+ rendered.push(`${styleText('bold', h3[1])}`);
181
+ continue;
182
+ }
183
+ if (line.trim().startsWith('- ') || line.trim().startsWith('* ')) {
184
+ rendered.push(` ${styleText('cyan', '•')} ${line.trim().slice(2)}`);
185
+ continue;
186
+ }
187
+ rendered.push(line);
188
+ }
189
+ return rendered.join('\n');
190
+ }
@@ -0,0 +1,2 @@
1
+ import { Tool } from './types.js';
2
+ export declare function createProjectTools(): Tool[];
@@ -0,0 +1,63 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { PermissionTier } from '../config/types.js';
4
+ export function createProjectTools() {
5
+ return [
6
+ {
7
+ definition: {
8
+ name: 'inspectProject',
9
+ description: 'Inspect project type and details',
10
+ tier: PermissionTier.ReadOnly,
11
+ parameters: []
12
+ },
13
+ handler: async () => {
14
+ const cwd = process.cwd();
15
+ const info = { type: 'unknown' };
16
+ try {
17
+ const pkgJson = await fs.readFile(path.join(cwd, 'package.json'), 'utf8');
18
+ const pkg = JSON.parse(pkgJson);
19
+ info.type = 'Node.js';
20
+ info.name = pkg.name;
21
+ info.scripts = pkg.scripts || {};
22
+ info.dependencies = Object.keys(pkg.dependencies || {});
23
+ const allDeps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
24
+ if (allDeps['react'])
25
+ info.framework = 'React';
26
+ if (allDeps['next'])
27
+ info.framework = 'Next.js';
28
+ if (allDeps['vue'])
29
+ info.framework = 'Vue';
30
+ if (allDeps['express'])
31
+ info.framework = 'Express';
32
+ return { success: true, output: JSON.stringify(info, null, 2) };
33
+ }
34
+ catch (e) { }
35
+ try {
36
+ await fs.readFile(path.join(cwd, 'Cargo.toml'));
37
+ info.type = 'Rust';
38
+ return { success: true, output: JSON.stringify(info, null, 2) };
39
+ }
40
+ catch (e) { }
41
+ try {
42
+ await fs.readFile(path.join(cwd, 'pyproject.toml'));
43
+ info.type = 'Python';
44
+ return { success: true, output: JSON.stringify(info, null, 2) };
45
+ }
46
+ catch (e) { }
47
+ try {
48
+ await fs.readFile(path.join(cwd, 'requirements.txt'));
49
+ info.type = 'Python';
50
+ return { success: true, output: JSON.stringify(info, null, 2) };
51
+ }
52
+ catch (e) { }
53
+ try {
54
+ await fs.readFile(path.join(cwd, 'go.mod'));
55
+ info.type = 'Go';
56
+ return { success: true, output: JSON.stringify(info, null, 2) };
57
+ }
58
+ catch (e) { }
59
+ return { success: true, output: JSON.stringify(info, null, 2) };
60
+ }
61
+ }
62
+ ];
63
+ }
@@ -0,0 +1,14 @@
1
+ import { Tool, ToolDefinition, ToolResult } from './types.js';
2
+ export declare function createWebSearchTools(): Tool[];
3
+ export declare function createMarkdownTools(): Tool[];
4
+ export declare function createSymbolAndImpactTools(): Tool[];
5
+ export declare function createCheckpointTools(): Tool[];
6
+ export declare class ToolRegistry {
7
+ private tools;
8
+ register(tool: Tool): void;
9
+ get(name: string): Tool | undefined;
10
+ list(): ToolDefinition[];
11
+ execute(name: string, params: Record<string, unknown>): Promise<ToolResult>;
12
+ toOpenAITools(): any[];
13
+ static createDefault(): ToolRegistry;
14
+ }
@@ -0,0 +1,230 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import { PermissionTier } from '../config/types.js';
3
+ import { createFilesystemTools } from './filesystem.js';
4
+ import { createSearchTools } from './search.js';
5
+ import { createShellTools } from './shell.js';
6
+ import { createGitTools } from './git.js';
7
+ import { createProjectTools } from './project.js';
8
+ import { createDiagnosticTools } from './diagnostics.js';
9
+ import { inspectMarkdown, patchMarkdownSection } from './markdown.js';
10
+ import { analyzeChangeImpact } from './symbols.js';
11
+ import { CheckpointManager } from './checkpoint.js';
12
+ import { mcpRegistry } from '../mcp/registry.js';
13
+ import { searchWeb } from './web_search.js';
14
+ export function createWebSearchTools() {
15
+ return [
16
+ {
17
+ definition: {
18
+ name: 'web_search',
19
+ description: 'Performs an online web search for technical documentation, error resolutions, APIs, or libraries without external dependencies.',
20
+ parameters: [
21
+ { name: 'query', type: 'string', description: 'The search query to look up on the web', required: true },
22
+ { name: 'maxResults', type: 'number', description: 'Maximum number of search results to return (default: 5)', required: false }
23
+ ],
24
+ tier: PermissionTier.ReadOnly
25
+ },
26
+ handler: async ({ query, maxResults }) => {
27
+ try {
28
+ const res = await searchWeb(String(query), maxResults ? Number(maxResults) : 5);
29
+ return {
30
+ success: true,
31
+ output: JSON.stringify(res, null, 2)
32
+ };
33
+ }
34
+ catch (err) {
35
+ return {
36
+ success: false,
37
+ output: '',
38
+ error: `Web search failed: ${err.message}`
39
+ };
40
+ }
41
+ }
42
+ }
43
+ ];
44
+ }
45
+ export function createMarkdownTools() {
46
+ return [
47
+ {
48
+ definition: {
49
+ name: 'markdown_inspect',
50
+ description: 'Parses a markdown file and returns its front matter, section heading outline, code blocks count, and tables.',
51
+ parameters: [
52
+ { name: 'filePath', type: 'string', description: 'Path to the markdown file to inspect', required: true }
53
+ ],
54
+ tier: PermissionTier.ReadOnly
55
+ },
56
+ handler: async ({ filePath }) => {
57
+ try {
58
+ const content = await fs.readFile(String(filePath), 'utf8');
59
+ const structure = inspectMarkdown(content);
60
+ return {
61
+ success: true,
62
+ output: JSON.stringify({
63
+ frontMatter: structure.frontMatter,
64
+ headings: structure.headings,
65
+ codeBlocksCount: structure.codeBlocksCount,
66
+ tablesCount: structure.tablesCount
67
+ }, null, 2)
68
+ };
69
+ }
70
+ catch (err) {
71
+ return { success: false, output: '', error: err.message };
72
+ }
73
+ }
74
+ },
75
+ {
76
+ definition: {
77
+ name: 'markdown_patch_section',
78
+ description: 'Surgically replaces only the content under a specific heading in a markdown document, preserving all surrounding headings, formatting, and tables.',
79
+ parameters: [
80
+ { name: 'filePath', type: 'string', description: 'Path to markdown file', required: true },
81
+ { name: 'heading', type: 'string', description: 'Target heading to update (e.g. "Installation" or "## Overview")', required: true },
82
+ { name: 'newContent', type: 'string', description: 'New content for this section', required: true }
83
+ ],
84
+ tier: PermissionTier.SafeMutation
85
+ },
86
+ handler: async ({ filePath, heading, newContent }) => {
87
+ return await patchMarkdownSection(String(filePath), String(heading), String(newContent));
88
+ }
89
+ }
90
+ ];
91
+ }
92
+ export function createSymbolAndImpactTools() {
93
+ return [
94
+ {
95
+ definition: {
96
+ name: 'analyze_change_impact',
97
+ description: 'Analyzes the blast radius of changing a file: extracts its symbols, finds referencing files, and discovers relevant test files.',
98
+ parameters: [
99
+ { name: 'filePath', type: 'string', description: 'Relative path to the file to analyze', required: true }
100
+ ],
101
+ tier: PermissionTier.ReadOnly
102
+ },
103
+ handler: async ({ filePath }) => {
104
+ const impact = await analyzeChangeImpact(process.cwd(), String(filePath));
105
+ return {
106
+ success: true,
107
+ output: JSON.stringify(impact, null, 2)
108
+ };
109
+ }
110
+ }
111
+ ];
112
+ }
113
+ export function createCheckpointTools() {
114
+ const chk = new CheckpointManager();
115
+ return [
116
+ {
117
+ definition: {
118
+ name: 'create_checkpoint',
119
+ description: 'Creates a safe recovery checkpoint of current workspace state before risky modifications.',
120
+ parameters: [
121
+ { name: 'name', type: 'string', description: 'Short label for the checkpoint', required: false }
122
+ ],
123
+ tier: PermissionTier.SafeMutation
124
+ },
125
+ handler: async ({ name }) => {
126
+ const info = await chk.create(name ? String(name) : 'checkpoint');
127
+ return {
128
+ success: true,
129
+ output: `Checkpoint created: ${info.name} (${info.id})`
130
+ };
131
+ }
132
+ },
133
+ {
134
+ definition: {
135
+ name: 'rollback_checkpoint',
136
+ description: 'Reverts workspace to a previous checkpoint.',
137
+ parameters: [
138
+ { name: 'checkpointId', type: 'string', description: 'Optional ID of checkpoint to revert to (defaults to latest)', required: false }
139
+ ],
140
+ tier: PermissionTier.StateMutating
141
+ },
142
+ handler: async ({ checkpointId }) => {
143
+ return await chk.rollback(checkpointId ? String(checkpointId) : undefined);
144
+ }
145
+ }
146
+ ];
147
+ }
148
+ export class ToolRegistry {
149
+ tools = new Map();
150
+ register(tool) {
151
+ this.tools.set(tool.definition.name, tool);
152
+ }
153
+ get(name) {
154
+ return this.tools.get(name);
155
+ }
156
+ list() {
157
+ return Array.from(this.tools.values()).map(tool => tool.definition);
158
+ }
159
+ async execute(name, params) {
160
+ const tool = this.get(name);
161
+ if (!tool) {
162
+ return {
163
+ success: false,
164
+ output: '',
165
+ error: `Tool not found: ${name}`
166
+ };
167
+ }
168
+ try {
169
+ return await tool.handler(params);
170
+ }
171
+ catch (err) {
172
+ return {
173
+ success: false,
174
+ output: '',
175
+ error: `Execution error: ${err.message}`
176
+ };
177
+ }
178
+ }
179
+ toOpenAITools() {
180
+ return Array.from(this.tools.values()).map(tool => {
181
+ const properties = {};
182
+ const required = [];
183
+ for (const param of tool.definition.parameters) {
184
+ properties[param.name] = {
185
+ type: param.type,
186
+ description: param.description,
187
+ };
188
+ if (param.enum)
189
+ properties[param.name].enum = param.enum;
190
+ if (param.items)
191
+ properties[param.name].items = param.items;
192
+ if (param.required) {
193
+ required.push(param.name);
194
+ }
195
+ }
196
+ return {
197
+ type: 'function',
198
+ function: {
199
+ name: tool.definition.name,
200
+ description: tool.definition.description,
201
+ parameters: {
202
+ type: 'object',
203
+ properties,
204
+ required
205
+ }
206
+ }
207
+ };
208
+ });
209
+ }
210
+ static createDefault() {
211
+ const registry = new ToolRegistry();
212
+ const allTools = [
213
+ ...createFilesystemTools(),
214
+ ...createSearchTools(),
215
+ ...createShellTools(),
216
+ ...createGitTools(),
217
+ ...createProjectTools(),
218
+ ...createDiagnosticTools(),
219
+ ...createWebSearchTools(),
220
+ ...createMarkdownTools(),
221
+ ...createSymbolAndImpactTools(),
222
+ ...createCheckpointTools(),
223
+ ...mcpRegistry.toOverarchTools(),
224
+ ];
225
+ for (const tool of allTools) {
226
+ registry.register(tool);
227
+ }
228
+ return registry;
229
+ }
230
+ }
@@ -0,0 +1,2 @@
1
+ import { Tool } from './types.js';
2
+ export declare function createSearchTools(): Tool[];
@@ -0,0 +1,136 @@
1
+ import * as fs from 'node:fs/promises';
2
+ import * as path from 'node:path';
3
+ import { execFile } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import { PermissionTier } from '../config/types.js';
6
+ const execFileAsync = promisify(execFile);
7
+ function simpleGlobMatch(pattern, text) {
8
+ if (!pattern)
9
+ return true;
10
+ const regexPattern = pattern
11
+ .replace(/[.+^${}()|[\]\\]/g, '\\$&')
12
+ .replace(/\*/g, '.*')
13
+ .replace(/\?/g, '.');
14
+ const regex = new RegExp(`^${regexPattern}$`);
15
+ return regex.test(text);
16
+ }
17
+ export function createSearchTools() {
18
+ return [
19
+ {
20
+ definition: {
21
+ name: 'grepSearch',
22
+ description: 'Search for text in files',
23
+ tier: PermissionTier.ReadOnly,
24
+ parameters: [
25
+ { name: 'pattern', type: 'string', description: 'Text to search for', required: true },
26
+ { name: 'path', type: 'string', description: 'Path to search in', required: false },
27
+ { name: 'include', type: 'string', description: 'Glob pattern to include', required: false },
28
+ { name: 'caseSensitive', type: 'boolean', description: 'Case sensitive search', required: false }
29
+ ]
30
+ },
31
+ handler: async (params) => {
32
+ const targetPath = params.path ? String(params.path) : '.';
33
+ const pattern = String(params.pattern);
34
+ const isWindows = process.platform === 'win32';
35
+ try {
36
+ let output = '';
37
+ if (isWindows) {
38
+ const args = ['/s', '/n'];
39
+ if (!params.caseSensitive)
40
+ args.push('/i');
41
+ args.push(pattern);
42
+ const searchPath = path.join(targetPath, params.include ? String(params.include) : '*.*');
43
+ args.push(searchPath);
44
+ try {
45
+ const { stdout } = await execFileAsync('findstr', args);
46
+ output = stdout;
47
+ }
48
+ catch (e) {
49
+ if (e.stdout)
50
+ output = e.stdout;
51
+ else
52
+ throw e;
53
+ }
54
+ }
55
+ else {
56
+ const args = ['-rn'];
57
+ if (!params.caseSensitive)
58
+ args.push('-i');
59
+ if (params.include) {
60
+ args.push('--include');
61
+ args.push(String(params.include));
62
+ }
63
+ args.push(pattern);
64
+ args.push(targetPath);
65
+ try {
66
+ const { stdout } = await execFileAsync('grep', args);
67
+ output = stdout;
68
+ }
69
+ catch (e) {
70
+ if (e.stdout)
71
+ output = e.stdout;
72
+ else
73
+ throw e;
74
+ }
75
+ }
76
+ const lines = output.split('\n').filter(Boolean).slice(0, 50);
77
+ return { success: true, output: lines.join('\n') || 'No matches found.' };
78
+ }
79
+ catch (e) {
80
+ return { success: false, output: '', error: e.message };
81
+ }
82
+ }
83
+ },
84
+ {
85
+ definition: {
86
+ name: 'findFiles',
87
+ description: 'Find files by pattern',
88
+ tier: PermissionTier.ReadOnly,
89
+ parameters: [
90
+ { name: 'pattern', type: 'string', description: 'Glob pattern', required: true },
91
+ { name: 'path', type: 'string', description: 'Search path', required: false },
92
+ { name: 'type', type: 'string', description: 'file, directory, or any', required: false, enum: ['file', 'directory', 'any'] }
93
+ ]
94
+ },
95
+ handler: async (params) => {
96
+ const root = params.path ? String(params.path) : '.';
97
+ const pattern = String(params.pattern);
98
+ const type = params.type || 'any';
99
+ const results = [];
100
+ const ignoreDirs = ['node_modules', '.git', 'dist', 'build'];
101
+ let count = 0;
102
+ async function walk(currentPath) {
103
+ if (count >= 50)
104
+ return;
105
+ try {
106
+ const entries = await fs.readdir(currentPath, { withFileTypes: true });
107
+ for (const entry of entries) {
108
+ if (count >= 50)
109
+ break;
110
+ if (entry.isDirectory() && ignoreDirs.includes(entry.name))
111
+ continue;
112
+ const fullPath = path.join(currentPath, entry.name);
113
+ let matchType = false;
114
+ if (type === 'any')
115
+ matchType = true;
116
+ else if (type === 'file' && entry.isFile())
117
+ matchType = true;
118
+ else if (type === 'directory' && entry.isDirectory())
119
+ matchType = true;
120
+ if (matchType && simpleGlobMatch(pattern, entry.name)) {
121
+ results.push(fullPath);
122
+ count++;
123
+ }
124
+ if (entry.isDirectory()) {
125
+ await walk(fullPath);
126
+ }
127
+ }
128
+ }
129
+ catch (e) { }
130
+ }
131
+ await walk(root);
132
+ return { success: true, output: results.join('\n') || 'No files found.' };
133
+ }
134
+ }
135
+ ];
136
+ }
@@ -0,0 +1,2 @@
1
+ import { Tool } from './types.js';
2
+ export declare function createShellTools(): Tool[];