@yuaone/tools 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +663 -0
- package/README.md +15 -0
- package/dist/__tests__/file-edit.test.d.ts +8 -0
- package/dist/__tests__/file-edit.test.d.ts.map +1 -0
- package/dist/__tests__/file-edit.test.js +125 -0
- package/dist/__tests__/file-edit.test.js.map +1 -0
- package/dist/__tests__/registry.test.d.ts +7 -0
- package/dist/__tests__/registry.test.d.ts.map +1 -0
- package/dist/__tests__/registry.test.js +83 -0
- package/dist/__tests__/registry.test.js.map +1 -0
- package/dist/__tests__/validators.test.d.ts +8 -0
- package/dist/__tests__/validators.test.d.ts.map +1 -0
- package/dist/__tests__/validators.test.js +189 -0
- package/dist/__tests__/validators.test.js.map +1 -0
- package/dist/base-tool.d.ts +45 -0
- package/dist/base-tool.d.ts.map +1 -0
- package/dist/base-tool.js +87 -0
- package/dist/base-tool.js.map +1 -0
- package/dist/browser-tool.d.ts +39 -0
- package/dist/browser-tool.d.ts.map +1 -0
- package/dist/browser-tool.js +518 -0
- package/dist/browser-tool.js.map +1 -0
- package/dist/code-search.d.ts +42 -0
- package/dist/code-search.d.ts.map +1 -0
- package/dist/code-search.js +298 -0
- package/dist/code-search.js.map +1 -0
- package/dist/design-tools.d.ts +70 -0
- package/dist/design-tools.d.ts.map +1 -0
- package/dist/design-tools.js +471 -0
- package/dist/design-tools.js.map +1 -0
- package/dist/dev-server-manager.d.ts +32 -0
- package/dist/dev-server-manager.d.ts.map +1 -0
- package/dist/dev-server-manager.js +183 -0
- package/dist/dev-server-manager.js.map +1 -0
- package/dist/file-edit.d.ts +19 -0
- package/dist/file-edit.d.ts.map +1 -0
- package/dist/file-edit.js +217 -0
- package/dist/file-edit.js.map +1 -0
- package/dist/file-read.d.ts +19 -0
- package/dist/file-read.d.ts.map +1 -0
- package/dist/file-read.js +142 -0
- package/dist/file-read.js.map +1 -0
- package/dist/file-write.d.ts +18 -0
- package/dist/file-write.d.ts.map +1 -0
- package/dist/file-write.js +139 -0
- package/dist/file-write.js.map +1 -0
- package/dist/git-ops.d.ts +25 -0
- package/dist/git-ops.d.ts.map +1 -0
- package/dist/git-ops.js +219 -0
- package/dist/git-ops.js.map +1 -0
- package/dist/glob.d.ts +18 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +91 -0
- package/dist/glob.js.map +1 -0
- package/dist/grep.d.ts +19 -0
- package/dist/grep.d.ts.map +1 -0
- package/dist/grep.js +177 -0
- package/dist/grep.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/security-scan.d.ts +62 -0
- package/dist/security-scan.d.ts.map +1 -0
- package/dist/security-scan.js +445 -0
- package/dist/security-scan.js.map +1 -0
- package/dist/shell-exec.d.ts +20 -0
- package/dist/shell-exec.d.ts.map +1 -0
- package/dist/shell-exec.js +206 -0
- package/dist/shell-exec.js.map +1 -0
- package/dist/test-run.d.ts +51 -0
- package/dist/test-run.d.ts.map +1 -0
- package/dist/test-run.js +359 -0
- package/dist/test-run.js.map +1 -0
- package/dist/tool-registry.d.ts +70 -0
- package/dist/tool-registry.d.ts.map +1 -0
- package/dist/tool-registry.js +181 -0
- package/dist/tool-registry.js.map +1 -0
- package/dist/types.d.ts +137 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/dist/validators.d.ts +57 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +218 -0
- package/dist/validators.js.map +1 -0
- package/package.json +42 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — file_write tool
|
|
3
|
+
*
|
|
4
|
+
* Writes content to a file.
|
|
5
|
+
* - Auto-creates directories (mkdir -p)
|
|
6
|
+
* - Backs up existing files before overwrite (.yuan-backup)
|
|
7
|
+
* - Detects and warns about sensitive files (.env, credentials, etc.)
|
|
8
|
+
*/
|
|
9
|
+
import { readFile, mkdir, stat, copyFile, open as fsOpen } from 'node:fs/promises';
|
|
10
|
+
import { constants as fsConstants } from 'node:fs';
|
|
11
|
+
import { dirname } from 'node:path';
|
|
12
|
+
import { BaseTool } from './base-tool.js';
|
|
13
|
+
import { isSensitiveFile } from './validators.js';
|
|
14
|
+
export class FileWriteTool extends BaseTool {
|
|
15
|
+
name = 'file_write';
|
|
16
|
+
description = 'Write content to a file. Creates directories automatically. ' +
|
|
17
|
+
'Backs up existing files before overwrite.';
|
|
18
|
+
riskLevel = 'high';
|
|
19
|
+
parameters = {
|
|
20
|
+
path: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: 'Relative path from project root',
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
content: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'File content to write',
|
|
28
|
+
required: true,
|
|
29
|
+
},
|
|
30
|
+
createDirectories: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: 'Auto-create intermediate directories (default: true)',
|
|
33
|
+
required: false,
|
|
34
|
+
default: true,
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
async execute(args, workDir) {
|
|
38
|
+
const toolCallId = args._toolCallId ?? '';
|
|
39
|
+
const path = args.path;
|
|
40
|
+
const content = args.content;
|
|
41
|
+
const createDirectories = args.createDirectories ?? true;
|
|
42
|
+
if (!path) {
|
|
43
|
+
return this.fail(toolCallId, 'Missing required parameter: path');
|
|
44
|
+
}
|
|
45
|
+
if (content === undefined || content === null) {
|
|
46
|
+
return this.fail(toolCallId, 'Missing required parameter: content');
|
|
47
|
+
}
|
|
48
|
+
let resolvedPath;
|
|
49
|
+
try {
|
|
50
|
+
resolvedPath = this.validatePath(path, workDir);
|
|
51
|
+
}
|
|
52
|
+
catch (err) {
|
|
53
|
+
return this.fail(toolCallId, err.message);
|
|
54
|
+
}
|
|
55
|
+
// Sensitive file warning
|
|
56
|
+
if (isSensitiveFile(path)) {
|
|
57
|
+
return this.fail(toolCallId, `Sensitive file detected: "${path}". ` +
|
|
58
|
+
'Writing to credential/secret files is blocked for security.');
|
|
59
|
+
}
|
|
60
|
+
// Write size limit (10MB)
|
|
61
|
+
const contentStr = String(content);
|
|
62
|
+
const MAX_WRITE_SIZE = 10 * 1024 * 1024;
|
|
63
|
+
if (Buffer.byteLength(contentStr, 'utf-8') > MAX_WRITE_SIZE) {
|
|
64
|
+
return this.fail(toolCallId, `Content exceeds maximum write size (10MB)`);
|
|
65
|
+
}
|
|
66
|
+
// Check if file already exists
|
|
67
|
+
let existed = false;
|
|
68
|
+
try {
|
|
69
|
+
const s = await stat(resolvedPath);
|
|
70
|
+
if (s.isDirectory()) {
|
|
71
|
+
return this.fail(toolCallId, `Path is a directory: ${path}`);
|
|
72
|
+
}
|
|
73
|
+
existed = true;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// File doesn't exist — will be created
|
|
77
|
+
}
|
|
78
|
+
// Create directories if needed
|
|
79
|
+
if (createDirectories) {
|
|
80
|
+
try {
|
|
81
|
+
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
82
|
+
}
|
|
83
|
+
catch (err) {
|
|
84
|
+
return this.fail(toolCallId, `Failed to create directories: ${err.message}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
// Backup existing file
|
|
88
|
+
if (existed) {
|
|
89
|
+
try {
|
|
90
|
+
const backupPath = resolvedPath + '.yuan-backup';
|
|
91
|
+
await copyFile(resolvedPath, backupPath);
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
// Best-effort backup — don't block write on backup failure
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Write file using O_NOFOLLOW to atomically prevent symlink TOCTOU attacks.
|
|
98
|
+
// This eliminates the race window between symlink check and write.
|
|
99
|
+
try {
|
|
100
|
+
const flags = existed
|
|
101
|
+
? fsConstants.O_WRONLY | fsConstants.O_TRUNC | fsConstants.O_NOFOLLOW
|
|
102
|
+
: fsConstants.O_WRONLY | fsConstants.O_CREAT | fsConstants.O_TRUNC | fsConstants.O_NOFOLLOW;
|
|
103
|
+
const fh = await fsOpen(resolvedPath, flags, 0o644);
|
|
104
|
+
try {
|
|
105
|
+
await fh.writeFile(contentStr, 'utf-8');
|
|
106
|
+
}
|
|
107
|
+
finally {
|
|
108
|
+
await fh.close();
|
|
109
|
+
}
|
|
110
|
+
const bytesWritten = Buffer.byteLength(contentStr, 'utf-8');
|
|
111
|
+
let output = existed
|
|
112
|
+
? `File overwritten: ${path} (${bytesWritten} bytes, backup created)`
|
|
113
|
+
: `File created: ${path} (${bytesWritten} bytes)`;
|
|
114
|
+
// If the file existed, show a brief diff hint
|
|
115
|
+
if (existed) {
|
|
116
|
+
try {
|
|
117
|
+
const originalContent = await readFile(resolvedPath + '.yuan-backup', 'utf-8');
|
|
118
|
+
const origLines = originalContent.split('\n').length;
|
|
119
|
+
const newLines = contentStr.split('\n').length;
|
|
120
|
+
output += `\nPrevious: ${origLines} lines → New: ${newLines} lines`;
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
// Backup read failed — skip diff info
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return this.ok(toolCallId, output, {
|
|
127
|
+
bytesWritten,
|
|
128
|
+
created: !existed,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
const msg = err.code === 'ELOOP'
|
|
133
|
+
? `Refusing to write through symlink: ${path}`
|
|
134
|
+
: `Failed to write file: ${err.message}`;
|
|
135
|
+
return this.fail(toolCallId, msg);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=file-write.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-write.js","sourceRoot":"","sources":["../src/file-write.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — git_ops tool
|
|
3
|
+
*
|
|
4
|
+
* Git operations: status, diff, log, add, commit, create_branch, stash, restore.
|
|
5
|
+
* - commit/create_branch require approval (riskLevel dynamically elevated)
|
|
6
|
+
* - Uses execFile (no shell interpretation)
|
|
7
|
+
*/
|
|
8
|
+
import type { ParameterDef, RiskLevel, ToolResult } from './types.js';
|
|
9
|
+
import { BaseTool } from './base-tool.js';
|
|
10
|
+
export declare class GitOpsTool extends BaseTool {
|
|
11
|
+
readonly name = "git_ops";
|
|
12
|
+
readonly description: string;
|
|
13
|
+
readonly riskLevel: RiskLevel;
|
|
14
|
+
readonly parameters: Record<string, ParameterDef>;
|
|
15
|
+
execute(args: Record<string, unknown>, workDir: string): Promise<ToolResult>;
|
|
16
|
+
private gitStatus;
|
|
17
|
+
private gitDiff;
|
|
18
|
+
private gitLog;
|
|
19
|
+
private gitAdd;
|
|
20
|
+
private gitCommit;
|
|
21
|
+
private gitCreateBranch;
|
|
22
|
+
private gitStash;
|
|
23
|
+
private gitRestore;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=git-ops.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.d.ts","sourceRoot":"","sources":["../src/git-ops.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAK1C,qBAAa,UAAW,SAAQ,QAAQ;IACtC,QAAQ,CAAC,IAAI,aAAa;IAC1B,QAAQ,CAAC,WAAW,SAE2C;IAC/D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAY;IAEzC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CA6B/C;IAEI,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;YAuCpE,SAAS;YAMT,OAAO;YAqBP,MAAM;YAUN,MAAM;YAuCN,SAAS;YA0BT,eAAe;YAcf,QAAQ;YAUR,UAAU;CAkBzB"}
|
package/dist/git-ops.js
ADDED
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — git_ops tool
|
|
3
|
+
*
|
|
4
|
+
* Git operations: status, diff, log, add, commit, create_branch, stash, restore.
|
|
5
|
+
* - commit/create_branch require approval (riskLevel dynamically elevated)
|
|
6
|
+
* - Uses execFile (no shell interpretation)
|
|
7
|
+
*/
|
|
8
|
+
import { execFile } from 'node:child_process';
|
|
9
|
+
import { BaseTool } from './base-tool.js';
|
|
10
|
+
import { validatePath, isSensitiveFile } from './validators.js';
|
|
11
|
+
const GIT_TIMEOUT = 15_000; // 15s
|
|
12
|
+
export class GitOpsTool extends BaseTool {
|
|
13
|
+
name = 'git_ops';
|
|
14
|
+
description = 'Perform git operations: status, diff, log, add, commit, create_branch, stash, restore. ' +
|
|
15
|
+
'commit and create_branch operations require user approval.';
|
|
16
|
+
riskLevel = 'medium';
|
|
17
|
+
parameters = {
|
|
18
|
+
operation: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Git operation to perform',
|
|
21
|
+
required: true,
|
|
22
|
+
enum: ['status', 'diff', 'log', 'add', 'commit', 'create_branch', 'stash', 'restore'],
|
|
23
|
+
},
|
|
24
|
+
message: {
|
|
25
|
+
type: 'string',
|
|
26
|
+
description: 'Commit message (required for commit operation)',
|
|
27
|
+
required: false,
|
|
28
|
+
},
|
|
29
|
+
files: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
description: 'Files to add/commit (default: all changed files for add)',
|
|
32
|
+
required: false,
|
|
33
|
+
items: { type: 'string', description: 'File path' },
|
|
34
|
+
},
|
|
35
|
+
count: {
|
|
36
|
+
type: 'number',
|
|
37
|
+
description: 'Number of log entries (default: 10, for log operation)',
|
|
38
|
+
required: false,
|
|
39
|
+
default: 10,
|
|
40
|
+
},
|
|
41
|
+
branch: {
|
|
42
|
+
type: 'string',
|
|
43
|
+
description: 'Branch name (for create_branch operation)',
|
|
44
|
+
required: false,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
async execute(args, workDir) {
|
|
48
|
+
const toolCallId = args._toolCallId ?? '';
|
|
49
|
+
const operation = args.operation;
|
|
50
|
+
if (!operation) {
|
|
51
|
+
return this.fail(toolCallId, 'Missing required parameter: operation');
|
|
52
|
+
}
|
|
53
|
+
const validOps = ['status', 'diff', 'log', 'add', 'commit', 'create_branch', 'stash', 'restore'];
|
|
54
|
+
if (!validOps.includes(operation)) {
|
|
55
|
+
return this.fail(toolCallId, `Invalid operation: ${operation}. Must be one of: ${validOps.join(', ')}`);
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
switch (operation) {
|
|
59
|
+
case 'status':
|
|
60
|
+
return await this.gitStatus(toolCallId, workDir);
|
|
61
|
+
case 'diff':
|
|
62
|
+
return await this.gitDiff(toolCallId, workDir);
|
|
63
|
+
case 'log':
|
|
64
|
+
return await this.gitLog(toolCallId, workDir, args.count ?? 10);
|
|
65
|
+
case 'add':
|
|
66
|
+
return await this.gitAdd(toolCallId, workDir, args.files);
|
|
67
|
+
case 'commit':
|
|
68
|
+
return await this.gitCommit(toolCallId, workDir, args.message, args.files);
|
|
69
|
+
case 'create_branch':
|
|
70
|
+
return await this.gitCreateBranch(toolCallId, workDir, args.branch);
|
|
71
|
+
case 'stash':
|
|
72
|
+
return await this.gitStash(toolCallId, workDir, args.message);
|
|
73
|
+
case 'restore':
|
|
74
|
+
return await this.gitRestore(toolCallId, workDir, args.files);
|
|
75
|
+
default:
|
|
76
|
+
return this.fail(toolCallId, `Unknown operation: ${operation}`);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (err) {
|
|
80
|
+
return this.fail(toolCallId, `Git operation failed: ${err.message}`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async gitStatus(toolCallId, cwd) {
|
|
84
|
+
const result = await runGit(['status', '--porcelain', '-u'], cwd);
|
|
85
|
+
const output = result.stdout || '(clean working tree)';
|
|
86
|
+
return this.ok(toolCallId, output, { operation: 'status' });
|
|
87
|
+
}
|
|
88
|
+
async gitDiff(toolCallId, cwd) {
|
|
89
|
+
// Show both staged and unstaged changes
|
|
90
|
+
const [unstaged, staged] = await Promise.all([
|
|
91
|
+
runGit(['diff'], cwd),
|
|
92
|
+
runGit(['diff', '--cached'], cwd),
|
|
93
|
+
]);
|
|
94
|
+
let output = '';
|
|
95
|
+
if (staged.stdout) {
|
|
96
|
+
output += `[Staged changes]\n${staged.stdout}\n`;
|
|
97
|
+
}
|
|
98
|
+
if (unstaged.stdout) {
|
|
99
|
+
output += `[Unstaged changes]\n${unstaged.stdout}\n`;
|
|
100
|
+
}
|
|
101
|
+
if (!output) {
|
|
102
|
+
output = '(no changes)';
|
|
103
|
+
}
|
|
104
|
+
return this.ok(toolCallId, output, { operation: 'diff' });
|
|
105
|
+
}
|
|
106
|
+
async gitLog(toolCallId, cwd, count) {
|
|
107
|
+
const safeCount = Math.min(Math.max(1, count), 50);
|
|
108
|
+
const result = await runGit(['log', `--max-count=${safeCount}`, '--oneline', '--decorate'], cwd);
|
|
109
|
+
const output = result.stdout || '(no commits)';
|
|
110
|
+
return this.ok(toolCallId, output, { operation: 'log' });
|
|
111
|
+
}
|
|
112
|
+
async gitAdd(toolCallId, cwd, files) {
|
|
113
|
+
const gitArgs = ['add'];
|
|
114
|
+
if (files && files.length > 0) {
|
|
115
|
+
// Validate each file path
|
|
116
|
+
for (const f of files) {
|
|
117
|
+
try {
|
|
118
|
+
validatePath(f, cwd);
|
|
119
|
+
}
|
|
120
|
+
catch (err) {
|
|
121
|
+
return this.fail(toolCallId, err.message);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
gitArgs.push('--', ...files);
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
gitArgs.push('-A');
|
|
128
|
+
}
|
|
129
|
+
await runGit(gitArgs, cwd);
|
|
130
|
+
// Warn about sensitive files when using -A
|
|
131
|
+
if (!files || files.length === 0) {
|
|
132
|
+
const statusResult = await runGit(['status', '--porcelain', '-u'], cwd);
|
|
133
|
+
const stagedFiles = statusResult.stdout.split('\n').filter(Boolean);
|
|
134
|
+
const sensitiveFiles = stagedFiles
|
|
135
|
+
.map((line) => line.slice(3).trim())
|
|
136
|
+
.filter((f) => isSensitiveFile(f));
|
|
137
|
+
if (sensitiveFiles.length > 0) {
|
|
138
|
+
return this.ok(toolCallId, `Files staged.\n${statusResult.stdout}\n\n⚠️ WARNING: Sensitive files staged: ${sensitiveFiles.join(', ')}. Consider unstaging them before commit.`, { operation: 'add', sensitiveFiles });
|
|
139
|
+
}
|
|
140
|
+
return this.ok(toolCallId, `Files staged.\n${statusResult.stdout}`, { operation: 'add' });
|
|
141
|
+
}
|
|
142
|
+
const statusResult = await runGit(['status', '--porcelain', '-u'], cwd);
|
|
143
|
+
return this.ok(toolCallId, `Files staged.\n${statusResult.stdout}`, { operation: 'add' });
|
|
144
|
+
}
|
|
145
|
+
async gitCommit(toolCallId, cwd, message, files) {
|
|
146
|
+
if (!message) {
|
|
147
|
+
return this.fail(toolCallId, 'Missing required parameter: message (for commit operation)');
|
|
148
|
+
}
|
|
149
|
+
// If specific files provided, validate paths and add them first
|
|
150
|
+
if (files && files.length > 0) {
|
|
151
|
+
for (const f of files) {
|
|
152
|
+
try {
|
|
153
|
+
validatePath(f, cwd);
|
|
154
|
+
}
|
|
155
|
+
catch (err) {
|
|
156
|
+
return this.fail(toolCallId, err.message);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
await runGit(['add', '--', ...files], cwd);
|
|
160
|
+
}
|
|
161
|
+
const result = await runGit(['commit', '-m', message], cwd);
|
|
162
|
+
return this.ok(toolCallId, result.stdout || 'Commit created.', { operation: 'commit' });
|
|
163
|
+
}
|
|
164
|
+
async gitCreateBranch(toolCallId, cwd, branch) {
|
|
165
|
+
if (!branch) {
|
|
166
|
+
return this.fail(toolCallId, 'Missing required parameter: branch (for create_branch operation)');
|
|
167
|
+
}
|
|
168
|
+
// Validate branch name: no spaces, no shell metacharacters
|
|
169
|
+
if (!/^[a-zA-Z0-9._\-/]+$/.test(branch)) {
|
|
170
|
+
return this.fail(toolCallId, `Invalid branch name: "${branch}". Use alphanumeric, dots, hyphens, underscores, and slashes only.`);
|
|
171
|
+
}
|
|
172
|
+
const result = await runGit(['checkout', '-b', branch], cwd);
|
|
173
|
+
return this.ok(toolCallId, result.stdout || `Branch "${branch}" created and checked out.`, { operation: 'create_branch' });
|
|
174
|
+
}
|
|
175
|
+
async gitStash(toolCallId, cwd, message) {
|
|
176
|
+
const gitArgs = ['stash', 'push'];
|
|
177
|
+
if (message) {
|
|
178
|
+
gitArgs.push('-m', message);
|
|
179
|
+
}
|
|
180
|
+
const result = await runGit(gitArgs, cwd);
|
|
181
|
+
return this.ok(toolCallId, result.stdout || 'Changes stashed.', { operation: 'stash' });
|
|
182
|
+
}
|
|
183
|
+
async gitRestore(toolCallId, cwd, files) {
|
|
184
|
+
const gitArgs = ['restore'];
|
|
185
|
+
if (files && files.length > 0) {
|
|
186
|
+
// Validate each file path to prevent argument injection
|
|
187
|
+
for (const f of files) {
|
|
188
|
+
try {
|
|
189
|
+
validatePath(f, cwd);
|
|
190
|
+
}
|
|
191
|
+
catch (err) {
|
|
192
|
+
return this.fail(toolCallId, err.message);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
gitArgs.push('--', ...files);
|
|
196
|
+
}
|
|
197
|
+
else {
|
|
198
|
+
gitArgs.push('.');
|
|
199
|
+
}
|
|
200
|
+
const result = await runGit(gitArgs, cwd);
|
|
201
|
+
return this.ok(toolCallId, result.stdout || 'Files restored.', { operation: 'restore' });
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
function runGit(args, cwd) {
|
|
205
|
+
return new Promise((resolve, reject) => {
|
|
206
|
+
execFile('git', args, { cwd, timeout: GIT_TIMEOUT, maxBuffer: 1024 * 1024 }, (error, stdout, stderr) => {
|
|
207
|
+
if (error && !stdout && !stderr) {
|
|
208
|
+
reject(error);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
resolve({
|
|
212
|
+
stdout: String(stdout ?? '').trim(),
|
|
213
|
+
stderr: String(stderr ?? '').trim(),
|
|
214
|
+
exitCode: error ? (typeof error.code === 'number' ? error.code : 1) : 0,
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
//# sourceMappingURL=git-ops.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-ops.js","sourceRoot":"","sources":["../src/git-ops.ts"],"names":[],"mappings":"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"}
|
package/dist/glob.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — glob tool
|
|
3
|
+
*
|
|
4
|
+
* Finds files matching glob patterns.
|
|
5
|
+
* - Uses fast-glob for performance
|
|
6
|
+
* - Auto-excludes node_modules, .git
|
|
7
|
+
* - Max 100 results by default
|
|
8
|
+
*/
|
|
9
|
+
import type { ParameterDef, RiskLevel, ToolResult } from './types.js';
|
|
10
|
+
import { BaseTool } from './base-tool.js';
|
|
11
|
+
export declare class GlobTool extends BaseTool {
|
|
12
|
+
readonly name = "glob";
|
|
13
|
+
readonly description: string;
|
|
14
|
+
readonly riskLevel: RiskLevel;
|
|
15
|
+
readonly parameters: Record<string, ParameterDef>;
|
|
16
|
+
execute(args: Record<string, unknown>, workDir: string): Promise<ToolResult>;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=glob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAI1C,qBAAa,QAAS,SAAQ,QAAQ;IACpC,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,WAAW,SAEiC;IACrD,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAS;IAEtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAiB/C;IAEI,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CA0DnF"}
|
package/dist/glob.js
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — glob tool
|
|
3
|
+
*
|
|
4
|
+
* Finds files matching glob patterns.
|
|
5
|
+
* - Uses fast-glob for performance
|
|
6
|
+
* - Auto-excludes node_modules, .git
|
|
7
|
+
* - Max 100 results by default
|
|
8
|
+
*/
|
|
9
|
+
import fg from 'fast-glob';
|
|
10
|
+
import { BaseTool } from './base-tool.js';
|
|
11
|
+
const DEFAULT_MAX_RESULTS = 100;
|
|
12
|
+
export class GlobTool extends BaseTool {
|
|
13
|
+
name = 'glob';
|
|
14
|
+
description = 'Find files matching a glob pattern. ' +
|
|
15
|
+
'Auto-excludes node_modules and .git directories.';
|
|
16
|
+
riskLevel = 'low';
|
|
17
|
+
parameters = {
|
|
18
|
+
pattern: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Glob pattern (e.g., "src/**/*.tsx", "**/*.test.ts")',
|
|
21
|
+
required: true,
|
|
22
|
+
},
|
|
23
|
+
path: {
|
|
24
|
+
type: 'string',
|
|
25
|
+
description: 'Base directory relative to project root (default: project root)',
|
|
26
|
+
required: false,
|
|
27
|
+
},
|
|
28
|
+
maxResults: {
|
|
29
|
+
type: 'number',
|
|
30
|
+
description: `Maximum results (default: ${DEFAULT_MAX_RESULTS})`,
|
|
31
|
+
required: false,
|
|
32
|
+
default: DEFAULT_MAX_RESULTS,
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
async execute(args, workDir) {
|
|
36
|
+
const toolCallId = args._toolCallId ?? '';
|
|
37
|
+
const pattern = args.pattern;
|
|
38
|
+
const basePath = args.path;
|
|
39
|
+
const maxResults = args.maxResults ?? DEFAULT_MAX_RESULTS;
|
|
40
|
+
if (!pattern) {
|
|
41
|
+
return this.fail(toolCallId, 'Missing required parameter: pattern');
|
|
42
|
+
}
|
|
43
|
+
// Resolve base directory
|
|
44
|
+
let cwd = workDir;
|
|
45
|
+
if (basePath) {
|
|
46
|
+
try {
|
|
47
|
+
cwd = this.validatePath(basePath, workDir);
|
|
48
|
+
}
|
|
49
|
+
catch (err) {
|
|
50
|
+
return this.fail(toolCallId, err.message);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
try {
|
|
54
|
+
const allFiles = await fg(pattern, {
|
|
55
|
+
cwd,
|
|
56
|
+
ignore: [
|
|
57
|
+
'**/node_modules/**',
|
|
58
|
+
'**/.git/**',
|
|
59
|
+
'**/dist/**',
|
|
60
|
+
'**/build/**',
|
|
61
|
+
'**/.next/**',
|
|
62
|
+
'**/coverage/**',
|
|
63
|
+
],
|
|
64
|
+
onlyFiles: true,
|
|
65
|
+
followSymbolicLinks: false,
|
|
66
|
+
});
|
|
67
|
+
const totalMatches = allFiles.length;
|
|
68
|
+
const truncated = totalMatches > maxResults;
|
|
69
|
+
const files = allFiles.slice(0, maxResults);
|
|
70
|
+
let output;
|
|
71
|
+
if (files.length === 0) {
|
|
72
|
+
output = `No files found matching pattern: ${pattern}`;
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
output = files.join('\n');
|
|
76
|
+
if (truncated) {
|
|
77
|
+
output += `\n\n... (showing ${maxResults} of ${totalMatches} total matches)`;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return this.ok(toolCallId, output, {
|
|
81
|
+
totalMatches,
|
|
82
|
+
filesReturned: files.length,
|
|
83
|
+
truncated,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
return this.fail(toolCallId, `Glob search failed: ${err.message}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=glob.js.map
|
package/dist/glob.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"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"}
|
package/dist/grep.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @yuaone/tools — grep tool
|
|
3
|
+
*
|
|
4
|
+
* Searches file contents using regex patterns.
|
|
5
|
+
* - Node.js built-in implementation (no ripgrep dependency)
|
|
6
|
+
* - Glob-based file filtering
|
|
7
|
+
* - Context lines support
|
|
8
|
+
* - Max 100 result lines
|
|
9
|
+
*/
|
|
10
|
+
import type { ParameterDef, RiskLevel, ToolResult } from './types.js';
|
|
11
|
+
import { BaseTool } from './base-tool.js';
|
|
12
|
+
export declare class GrepTool extends BaseTool {
|
|
13
|
+
readonly name = "grep";
|
|
14
|
+
readonly description: string;
|
|
15
|
+
readonly riskLevel: RiskLevel;
|
|
16
|
+
readonly parameters: Record<string, ParameterDef>;
|
|
17
|
+
execute(args: Record<string, unknown>, workDir: string): Promise<ToolResult>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=grep.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep.d.ts","sourceRoot":"","sources":["../src/grep.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,UAAU,EAAa,MAAM,YAAY,CAAC;AACjF,OAAO,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAM1C,qBAAa,QAAS,SAAQ,QAAQ;IACpC,QAAQ,CAAC,IAAI,UAAU;IACvB,QAAQ,CAAC,WAAW,SAEyC;IAC7D,QAAQ,CAAC,SAAS,EAAE,SAAS,CAAS;IAEtC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CA4B/C;IAEI,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC;CAiJnF"}
|