beth-copilot 1.0.15 → 1.0.16

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 (136) hide show
  1. package/README.md +197 -77
  2. package/bin/cli.js +47 -0
  3. package/dist/core/context.d.ts +171 -0
  4. package/dist/core/context.d.ts.map +1 -0
  5. package/dist/core/context.js +353 -0
  6. package/dist/core/context.js.map +1 -0
  7. package/dist/core/context.test.d.ts +8 -0
  8. package/dist/core/context.test.d.ts.map +1 -0
  9. package/dist/core/context.test.js +253 -0
  10. package/dist/core/context.test.js.map +1 -0
  11. package/dist/core/handoffs.d.ts +151 -0
  12. package/dist/core/handoffs.d.ts.map +1 -0
  13. package/dist/core/handoffs.js +220 -0
  14. package/dist/core/handoffs.js.map +1 -0
  15. package/dist/core/handoffs.test.d.ts +8 -0
  16. package/dist/core/handoffs.test.d.ts.map +1 -0
  17. package/dist/core/handoffs.test.js +231 -0
  18. package/dist/core/handoffs.test.js.map +1 -0
  19. package/dist/core/orchestrator.d.ts +246 -0
  20. package/dist/core/orchestrator.d.ts.map +1 -0
  21. package/dist/core/orchestrator.js +514 -0
  22. package/dist/core/orchestrator.js.map +1 -0
  23. package/dist/core/orchestrator.test.d.ts +8 -0
  24. package/dist/core/orchestrator.test.d.ts.map +1 -0
  25. package/dist/core/orchestrator.test.js +517 -0
  26. package/dist/core/orchestrator.test.js.map +1 -0
  27. package/dist/core/router.d.ts +102 -0
  28. package/dist/core/router.d.ts.map +1 -0
  29. package/dist/core/router.js +178 -0
  30. package/dist/core/router.js.map +1 -0
  31. package/dist/core/router.test.d.ts +8 -0
  32. package/dist/core/router.test.d.ts.map +1 -0
  33. package/dist/core/router.test.js +215 -0
  34. package/dist/core/router.test.js.map +1 -0
  35. package/dist/index.d.ts +9 -0
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +7 -0
  38. package/dist/index.js.map +1 -1
  39. package/dist/tools/cli/beads.d.ts +27 -0
  40. package/dist/tools/cli/beads.d.ts.map +1 -0
  41. package/dist/tools/cli/beads.js +172 -0
  42. package/dist/tools/cli/beads.js.map +1 -0
  43. package/dist/tools/cli/beads.test.d.ts +8 -0
  44. package/dist/tools/cli/beads.test.d.ts.map +1 -0
  45. package/dist/tools/cli/beads.test.js +264 -0
  46. package/dist/tools/cli/beads.test.js.map +1 -0
  47. package/dist/tools/cli/editFile.d.ts +17 -0
  48. package/dist/tools/cli/editFile.d.ts.map +1 -0
  49. package/dist/tools/cli/editFile.js +125 -0
  50. package/dist/tools/cli/editFile.js.map +1 -0
  51. package/dist/tools/cli/editFile.test.d.ts +8 -0
  52. package/dist/tools/cli/editFile.test.d.ts.map +1 -0
  53. package/dist/tools/cli/editFile.test.js +177 -0
  54. package/dist/tools/cli/editFile.test.js.map +1 -0
  55. package/dist/tools/cli/readFile.d.ts +25 -0
  56. package/dist/tools/cli/readFile.d.ts.map +1 -0
  57. package/dist/tools/cli/readFile.js +118 -0
  58. package/dist/tools/cli/readFile.js.map +1 -0
  59. package/dist/tools/cli/readFile.test.d.ts +8 -0
  60. package/dist/tools/cli/readFile.test.d.ts.map +1 -0
  61. package/dist/tools/cli/readFile.test.js +194 -0
  62. package/dist/tools/cli/readFile.test.js.map +1 -0
  63. package/dist/tools/cli/search.d.ts +16 -0
  64. package/dist/tools/cli/search.d.ts.map +1 -0
  65. package/dist/tools/cli/search.js +261 -0
  66. package/dist/tools/cli/search.js.map +1 -0
  67. package/dist/tools/cli/search.test.d.ts +8 -0
  68. package/dist/tools/cli/search.test.d.ts.map +1 -0
  69. package/dist/tools/cli/search.test.js +172 -0
  70. package/dist/tools/cli/search.test.js.map +1 -0
  71. package/dist/tools/cli/subagent.d.ts +43 -0
  72. package/dist/tools/cli/subagent.d.ts.map +1 -0
  73. package/dist/tools/cli/subagent.js +99 -0
  74. package/dist/tools/cli/subagent.js.map +1 -0
  75. package/dist/tools/cli/subagent.test.d.ts +8 -0
  76. package/dist/tools/cli/subagent.test.d.ts.map +1 -0
  77. package/dist/tools/cli/subagent.test.js +190 -0
  78. package/dist/tools/cli/subagent.test.js.map +1 -0
  79. package/dist/tools/cli/terminal.d.ts +19 -0
  80. package/dist/tools/cli/terminal.d.ts.map +1 -0
  81. package/dist/tools/cli/terminal.js +164 -0
  82. package/dist/tools/cli/terminal.js.map +1 -0
  83. package/dist/tools/cli/terminal.test.d.ts +8 -0
  84. package/dist/tools/cli/terminal.test.d.ts.map +1 -0
  85. package/dist/tools/cli/terminal.test.js +161 -0
  86. package/dist/tools/cli/terminal.test.js.map +1 -0
  87. package/dist/tools/index.d.ts +25 -0
  88. package/dist/tools/index.d.ts.map +1 -0
  89. package/dist/tools/index.js +41 -0
  90. package/dist/tools/index.js.map +1 -0
  91. package/dist/tools/interface.d.ts +64 -0
  92. package/dist/tools/interface.d.ts.map +1 -0
  93. package/dist/tools/interface.js +37 -0
  94. package/dist/tools/interface.js.map +1 -0
  95. package/dist/tools/interface.test.d.ts +7 -0
  96. package/dist/tools/interface.test.d.ts.map +1 -0
  97. package/dist/tools/interface.test.js +179 -0
  98. package/dist/tools/interface.test.js.map +1 -0
  99. package/dist/tools/mcp/bridge.d.ts +48 -0
  100. package/dist/tools/mcp/bridge.d.ts.map +1 -0
  101. package/dist/tools/mcp/bridge.js +128 -0
  102. package/dist/tools/mcp/bridge.js.map +1 -0
  103. package/dist/tools/mcp/bridge.test.d.ts +8 -0
  104. package/dist/tools/mcp/bridge.test.d.ts.map +1 -0
  105. package/dist/tools/mcp/bridge.test.js +300 -0
  106. package/dist/tools/mcp/bridge.test.js.map +1 -0
  107. package/dist/tools/mcp/client.d.ts +135 -0
  108. package/dist/tools/mcp/client.d.ts.map +1 -0
  109. package/dist/tools/mcp/client.js +263 -0
  110. package/dist/tools/mcp/client.js.map +1 -0
  111. package/dist/tools/mcp/client.test.d.ts +8 -0
  112. package/dist/tools/mcp/client.test.d.ts.map +1 -0
  113. package/dist/tools/mcp/client.test.js +390 -0
  114. package/dist/tools/mcp/client.test.js.map +1 -0
  115. package/dist/tools/registry.d.ts +82 -0
  116. package/dist/tools/registry.d.ts.map +1 -0
  117. package/dist/tools/registry.js +99 -0
  118. package/dist/tools/registry.js.map +1 -0
  119. package/dist/tools/registry.test.d.ts +7 -0
  120. package/dist/tools/registry.test.d.ts.map +1 -0
  121. package/dist/tools/registry.test.js +199 -0
  122. package/dist/tools/registry.test.js.map +1 -0
  123. package/dist/tools/suite.test.d.ts +11 -0
  124. package/dist/tools/suite.test.d.ts.map +1 -0
  125. package/dist/tools/suite.test.js +119 -0
  126. package/dist/tools/suite.test.js.map +1 -0
  127. package/dist/tools/types.d.ts +75 -0
  128. package/dist/tools/types.d.ts.map +1 -0
  129. package/dist/tools/types.js +30 -0
  130. package/dist/tools/types.js.map +1 -0
  131. package/dist/tools/types.test.d.ts +7 -0
  132. package/dist/tools/types.test.d.ts.map +1 -0
  133. package/dist/tools/types.test.js +178 -0
  134. package/dist/tools/types.test.js.map +1 -0
  135. package/package.json +2 -2
  136. package/sbom.json +209 -209
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Beads Tool
3
+ *
4
+ * Wraps the `bd` CLI for issue tracking via the beads system.
5
+ * Supports creating, closing, listing, and inspecting issues
6
+ * and their dependency trees.
7
+ *
8
+ * @see https://github.com/steveyegge/beads
9
+ */
10
+ import { execFile as nodeExecFile } from 'node:child_process';
11
+ import { ToolError } from '../types.js';
12
+ /** Valid beads commands */
13
+ const BEADS_COMMANDS = ['create', 'close', 'list', 'ready', 'show', 'dep-tree'];
14
+ /** Input schema for the beads tool */
15
+ const inputSchema = {
16
+ type: 'object',
17
+ properties: {
18
+ command: {
19
+ type: 'string',
20
+ enum: [...BEADS_COMMANDS],
21
+ description: 'Beads command to execute',
22
+ },
23
+ args: {
24
+ type: 'object',
25
+ description: 'Command-specific arguments',
26
+ properties: {
27
+ title: { type: 'string', description: 'Issue title (for create)' },
28
+ description: { type: 'string', description: 'Issue description (for create)' },
29
+ type: { type: 'string', enum: ['task', 'epic'], description: 'Issue type (for create)' },
30
+ priority: { type: 'number', description: 'Priority level (for create)' },
31
+ parent: { type: 'string', description: 'Parent issue ID (for create)' },
32
+ deps: { type: 'string', description: 'Comma-separated dependency IDs (for create)' },
33
+ label: { type: 'string', description: 'Label to apply (for create)' },
34
+ id: { type: 'string', description: 'Issue ID (for close, show, dep-tree)' },
35
+ },
36
+ },
37
+ },
38
+ required: ['command'],
39
+ };
40
+ /**
41
+ * Build the `bd` CLI argument array for a given command and args.
42
+ *
43
+ * @param command - The beads command to run
44
+ * @param args - Command-specific arguments
45
+ * @param toolName - Tool name for error reporting
46
+ * @returns Array of string arguments to pass to `bd`
47
+ */
48
+ function buildArgs(command, args, toolName) {
49
+ switch (command) {
50
+ case 'create': {
51
+ const title = args?.title;
52
+ if (typeof title !== 'string' || title.trim().length === 0) {
53
+ throw new ToolError('title is required for create command', 'INVALID_INPUT', toolName);
54
+ }
55
+ const result = ['create', title];
56
+ if (typeof args?.description === 'string' && args.description.length > 0) {
57
+ result.push(`--description=${args.description}`);
58
+ }
59
+ if (args?.type === 'epic') {
60
+ result.push('--type', 'epic');
61
+ }
62
+ if (typeof args?.priority === 'number') {
63
+ result.push('-p', String(args.priority));
64
+ }
65
+ if (typeof args?.parent === 'string' && args.parent.length > 0) {
66
+ result.push('--parent', args.parent);
67
+ }
68
+ if (typeof args?.deps === 'string' && args.deps.length > 0) {
69
+ result.push('--deps', args.deps);
70
+ }
71
+ if (typeof args?.label === 'string' && args.label.length > 0) {
72
+ result.push('-l', args.label);
73
+ }
74
+ return result;
75
+ }
76
+ case 'close': {
77
+ const id = args?.id;
78
+ if (typeof id !== 'string' || id.trim().length === 0) {
79
+ throw new ToolError('id is required for close command', 'INVALID_INPUT', toolName);
80
+ }
81
+ return ['close', id];
82
+ }
83
+ case 'list':
84
+ return ['list'];
85
+ case 'ready':
86
+ return ['ready'];
87
+ case 'show': {
88
+ const id = args?.id;
89
+ if (typeof id !== 'string' || id.trim().length === 0) {
90
+ throw new ToolError('id is required for show command', 'INVALID_INPUT', toolName);
91
+ }
92
+ return ['show', id];
93
+ }
94
+ case 'dep-tree': {
95
+ const id = args?.id;
96
+ if (typeof id !== 'string' || id.trim().length === 0) {
97
+ throw new ToolError('id is required for dep-tree command', 'INVALID_INPUT', toolName);
98
+ }
99
+ return ['dep', 'tree', id];
100
+ }
101
+ }
102
+ }
103
+ /**
104
+ * Create a beads tool instance.
105
+ *
106
+ * Accepts an optional `execFn` for dependency injection in tests.
107
+ * When omitted, uses `node:child_process.execFile`.
108
+ *
109
+ * @param execFn - Optional execFile function for testing
110
+ * @returns A Tool implementation for beads
111
+ */
112
+ export function createBeadsTool(execFn) {
113
+ const exec = execFn ?? nodeExecFile;
114
+ return {
115
+ name: 'beads',
116
+ description: 'Manage issues and work tracking via the beads (bd) CLI',
117
+ inputSchema,
118
+ async execute(input, context) {
119
+ // Check permission — beads runs as a CLI command
120
+ if (!context.permissions.allowTerminal) {
121
+ throw new ToolError('Terminal permission denied', 'PERMISSION_DENIED', 'beads');
122
+ }
123
+ // Validate command
124
+ const command = input.command;
125
+ if (typeof command !== 'string' || !BEADS_COMMANDS.includes(command)) {
126
+ throw new ToolError(`command must be one of: ${BEADS_COMMANDS.join(', ')}`, 'INVALID_INPUT', 'beads');
127
+ }
128
+ const args = (input.args ?? undefined);
129
+ const bdArgs = buildArgs(command, args, 'beads');
130
+ // Verify bd is available
131
+ await new Promise((resolve, reject) => {
132
+ exec('bd', ['--version'], { timeout: 5000 }, (error) => {
133
+ if (error) {
134
+ reject(new ToolError('beads (bd) CLI not found. Install from https://github.com/steveyegge/beads', 'NOT_FOUND', 'beads', { cause: error }));
135
+ }
136
+ else {
137
+ resolve();
138
+ }
139
+ });
140
+ });
141
+ // Execute the bd command
142
+ return new Promise((resolve, reject) => {
143
+ exec('bd', bdArgs, { cwd: context.workingDir, timeout: 30_000 }, (error, stdout, stderr) => {
144
+ let output = '';
145
+ if (stdout)
146
+ output += stdout;
147
+ if (stderr) {
148
+ if (output.length > 0 && !output.endsWith('\n'))
149
+ output += '\n';
150
+ if (stderr.length > 0)
151
+ output += stderr;
152
+ }
153
+ if (error) {
154
+ reject(new ToolError(`bd ${command} failed: ${output || error.message}`, 'EXECUTION_FAILED', 'beads', { cause: error }));
155
+ return;
156
+ }
157
+ resolve({
158
+ success: true,
159
+ output: output.trim(),
160
+ metadata: {
161
+ command,
162
+ args: bdArgs,
163
+ },
164
+ });
165
+ });
166
+ });
167
+ },
168
+ };
169
+ }
170
+ /** Default beads tool instance using real execFile */
171
+ export const beadsTool = createBeadsTool();
172
+ //# sourceMappingURL=beads.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beads.js","sourceRoot":"","sources":["../../../src/tools/cli/beads.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Beads Tool Tests
3
+ *
4
+ * Tests for the beads tool implementation.
5
+ * Uses dependency injection (createBeadsTool) to mock execFile.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=beads.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beads.test.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/beads.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Beads Tool Tests
3
+ *
4
+ * Tests for the beads tool implementation.
5
+ * Uses dependency injection (createBeadsTool) to mock execFile.
6
+ */
7
+ import { describe, it } from 'node:test';
8
+ import assert from 'node:assert';
9
+ import { createBeadsTool } from './beads.js';
10
+ import { ToolError } from '../types.js';
11
+ /** Create a ToolContext with sensible defaults */
12
+ function createContext(overrides) {
13
+ return {
14
+ workingDir: '/tmp/test',
15
+ permissions: overrides?.permissions ?? {
16
+ allowFileRead: false,
17
+ allowFileWrite: false,
18
+ allowTerminal: true,
19
+ allowNetwork: false,
20
+ },
21
+ signal: overrides?.signal,
22
+ };
23
+ }
24
+ /**
25
+ * Create a mock execFile function that records calls and returns configured output.
26
+ *
27
+ * @param responses - Map of first-arg command to { stdout, stderr, error }
28
+ * @returns [mockFn, calls] — the mock function and an array of recorded call args
29
+ */
30
+ function createMockExec(responses) {
31
+ const calls = [];
32
+ const mockFn = (file, args, _options, callback) => {
33
+ calls.push({ file, args: [...args] });
34
+ // First call is --version check, subsequent calls are the actual command
35
+ const isVersionCheck = args.includes('--version');
36
+ const response = isVersionCheck
37
+ ? (responses?.version ?? { stdout: 'beads 1.0.0', stderr: '', error: null })
38
+ : (responses?.command ?? { stdout: 'ok', stderr: '', error: null });
39
+ // Use setImmediate to keep callback async like real execFile
40
+ setImmediate(() => {
41
+ callback(response.error ?? null, response.stdout ?? '', response.stderr ?? '');
42
+ });
43
+ // Return a minimal ChildProcess-like object
44
+ return { kill: () => { } };
45
+ };
46
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
47
+ return [mockFn, calls];
48
+ }
49
+ describe('beads tool', () => {
50
+ describe('metadata', () => {
51
+ it('should have name "beads"', () => {
52
+ const [mockFn] = createMockExec();
53
+ const tool = createBeadsTool(mockFn);
54
+ assert.strictEqual(tool.name, 'beads');
55
+ });
56
+ it('should have a description', () => {
57
+ const [mockFn] = createMockExec();
58
+ const tool = createBeadsTool(mockFn);
59
+ assert.ok(tool.description.length > 0);
60
+ });
61
+ it('should have an inputSchema with command required', () => {
62
+ const [mockFn] = createMockExec();
63
+ const tool = createBeadsTool(mockFn);
64
+ assert.strictEqual(tool.inputSchema.type, 'object');
65
+ assert.ok(Array.isArray(tool.inputSchema.required));
66
+ assert.ok(tool.inputSchema.required.includes('command'));
67
+ });
68
+ });
69
+ describe('command building — create', () => {
70
+ it('should build args for create with title only', async () => {
71
+ const [mockFn, calls] = createMockExec();
72
+ const tool = createBeadsTool(mockFn);
73
+ await tool.execute({ command: 'create', args: { title: 'Fix bug' } }, createContext());
74
+ // Second call is the actual command (first is --version)
75
+ assert.deepStrictEqual(calls[1].args, ['create', 'Fix bug']);
76
+ });
77
+ it('should build args for create with all optional args', async () => {
78
+ const [mockFn, calls] = createMockExec();
79
+ const tool = createBeadsTool(mockFn);
80
+ await tool.execute({
81
+ command: 'create',
82
+ args: {
83
+ title: 'Feature X',
84
+ description: 'Build feature X',
85
+ type: 'epic',
86
+ priority: 1,
87
+ parent: 'abc-123',
88
+ deps: 'dep-1,dep-2',
89
+ label: 'in_progress',
90
+ },
91
+ }, createContext());
92
+ const cmdArgs = calls[1].args;
93
+ assert.strictEqual(cmdArgs[0], 'create');
94
+ assert.strictEqual(cmdArgs[1], 'Feature X');
95
+ assert.ok(cmdArgs.includes('--description=Build feature X'));
96
+ assert.ok(cmdArgs.includes('--type'));
97
+ assert.ok(cmdArgs.includes('epic'));
98
+ assert.ok(cmdArgs.includes('-p'));
99
+ assert.ok(cmdArgs.includes('1'));
100
+ assert.ok(cmdArgs.includes('--parent'));
101
+ assert.ok(cmdArgs.includes('abc-123'));
102
+ assert.ok(cmdArgs.includes('--deps'));
103
+ assert.ok(cmdArgs.includes('dep-1,dep-2'));
104
+ assert.ok(cmdArgs.includes('-l'));
105
+ assert.ok(cmdArgs.includes('in_progress'));
106
+ });
107
+ it('should reject create without title', async () => {
108
+ const [mockFn] = createMockExec();
109
+ const tool = createBeadsTool(mockFn);
110
+ await assert.rejects(() => tool.execute({ command: 'create', args: {} }, createContext()), (err) => {
111
+ assert.ok(err instanceof ToolError);
112
+ assert.strictEqual(err.code, 'INVALID_INPUT');
113
+ assert.ok(err.message.includes('title'));
114
+ return true;
115
+ });
116
+ });
117
+ });
118
+ describe('command building — close', () => {
119
+ it('should build args for close', async () => {
120
+ const [mockFn, calls] = createMockExec();
121
+ const tool = createBeadsTool(mockFn);
122
+ await tool.execute({ command: 'close', args: { id: 'issue-1' } }, createContext());
123
+ assert.deepStrictEqual(calls[1].args, ['close', 'issue-1']);
124
+ });
125
+ it('should reject close without id', async () => {
126
+ const [mockFn] = createMockExec();
127
+ const tool = createBeadsTool(mockFn);
128
+ await assert.rejects(() => tool.execute({ command: 'close', args: {} }, createContext()), (err) => {
129
+ assert.ok(err instanceof ToolError);
130
+ assert.strictEqual(err.code, 'INVALID_INPUT');
131
+ assert.ok(err.message.includes('id'));
132
+ return true;
133
+ });
134
+ });
135
+ });
136
+ describe('command building — list', () => {
137
+ it('should build args for list', async () => {
138
+ const [mockFn, calls] = createMockExec();
139
+ const tool = createBeadsTool(mockFn);
140
+ await tool.execute({ command: 'list' }, createContext());
141
+ assert.deepStrictEqual(calls[1].args, ['list']);
142
+ });
143
+ });
144
+ describe('command building — ready', () => {
145
+ it('should build args for ready', async () => {
146
+ const [mockFn, calls] = createMockExec();
147
+ const tool = createBeadsTool(mockFn);
148
+ await tool.execute({ command: 'ready' }, createContext());
149
+ assert.deepStrictEqual(calls[1].args, ['ready']);
150
+ });
151
+ });
152
+ describe('command building — show', () => {
153
+ it('should build args for show', async () => {
154
+ const [mockFn, calls] = createMockExec();
155
+ const tool = createBeadsTool(mockFn);
156
+ await tool.execute({ command: 'show', args: { id: 'issue-2' } }, createContext());
157
+ assert.deepStrictEqual(calls[1].args, ['show', 'issue-2']);
158
+ });
159
+ it('should reject show without id', async () => {
160
+ const [mockFn] = createMockExec();
161
+ const tool = createBeadsTool(mockFn);
162
+ await assert.rejects(() => tool.execute({ command: 'show', args: {} }, createContext()), (err) => {
163
+ assert.ok(err instanceof ToolError);
164
+ assert.strictEqual(err.code, 'INVALID_INPUT');
165
+ assert.ok(err.message.includes('id'));
166
+ return true;
167
+ });
168
+ });
169
+ });
170
+ describe('command building — dep-tree', () => {
171
+ it('should build args for dep-tree', async () => {
172
+ const [mockFn, calls] = createMockExec();
173
+ const tool = createBeadsTool(mockFn);
174
+ await tool.execute({ command: 'dep-tree', args: { id: 'issue-3' } }, createContext());
175
+ assert.deepStrictEqual(calls[1].args, ['dep', 'tree', 'issue-3']);
176
+ });
177
+ it('should reject dep-tree without id', async () => {
178
+ const [mockFn] = createMockExec();
179
+ const tool = createBeadsTool(mockFn);
180
+ await assert.rejects(() => tool.execute({ command: 'dep-tree', args: {} }, createContext()), (err) => {
181
+ assert.ok(err instanceof ToolError);
182
+ assert.strictEqual(err.code, 'INVALID_INPUT');
183
+ assert.ok(err.message.includes('id'));
184
+ return true;
185
+ });
186
+ });
187
+ });
188
+ describe('permissions', () => {
189
+ it('should throw PERMISSION_DENIED when allowTerminal is false', async () => {
190
+ const [mockFn] = createMockExec();
191
+ const tool = createBeadsTool(mockFn);
192
+ const ctx = createContext({
193
+ permissions: {
194
+ allowFileRead: false,
195
+ allowFileWrite: false,
196
+ allowTerminal: false,
197
+ allowNetwork: false,
198
+ },
199
+ });
200
+ await assert.rejects(() => tool.execute({ command: 'list' }, ctx), (err) => {
201
+ assert.ok(err instanceof ToolError);
202
+ assert.strictEqual(err.code, 'PERMISSION_DENIED');
203
+ return true;
204
+ });
205
+ });
206
+ });
207
+ describe('bd not installed', () => {
208
+ it('should throw NOT_FOUND when bd --version fails', async () => {
209
+ const [mockFn] = createMockExec({
210
+ version: { error: new Error('command not found: bd'), stdout: '', stderr: '' },
211
+ });
212
+ const tool = createBeadsTool(mockFn);
213
+ await assert.rejects(() => tool.execute({ command: 'list' }, createContext()), (err) => {
214
+ assert.ok(err instanceof ToolError);
215
+ assert.strictEqual(err.code, 'NOT_FOUND');
216
+ assert.ok(err.message.includes('bd'));
217
+ return true;
218
+ });
219
+ });
220
+ });
221
+ describe('command execution failure', () => {
222
+ it('should throw EXECUTION_FAILED on bd errors', async () => {
223
+ const [mockFn] = createMockExec({
224
+ command: { error: new Error('bd close failed'), stdout: '', stderr: 'no such issue' },
225
+ });
226
+ const tool = createBeadsTool(mockFn);
227
+ await assert.rejects(() => tool.execute({ command: 'close', args: { id: 'bad-id' } }, createContext()), (err) => {
228
+ assert.ok(err instanceof ToolError);
229
+ assert.strictEqual(err.code, 'EXECUTION_FAILED');
230
+ return true;
231
+ });
232
+ });
233
+ });
234
+ describe('invalid command', () => {
235
+ it('should throw INVALID_INPUT for unknown command', async () => {
236
+ const [mockFn] = createMockExec();
237
+ const tool = createBeadsTool(mockFn);
238
+ await assert.rejects(() => tool.execute({ command: 'purge' }, createContext()), (err) => {
239
+ assert.ok(err instanceof ToolError);
240
+ assert.strictEqual(err.code, 'INVALID_INPUT');
241
+ return true;
242
+ });
243
+ });
244
+ });
245
+ describe('successful output', () => {
246
+ it('should return success with trimmed output', async () => {
247
+ const [mockFn] = createMockExec({
248
+ command: { stdout: ' issue-1: Fix bug\n', stderr: '', error: null },
249
+ });
250
+ const tool = createBeadsTool(mockFn);
251
+ const result = await tool.execute({ command: 'list' }, createContext());
252
+ assert.strictEqual(result.success, true);
253
+ assert.strictEqual(result.output, 'issue-1: Fix bug');
254
+ });
255
+ it('should include command and args in metadata', async () => {
256
+ const [mockFn] = createMockExec();
257
+ const tool = createBeadsTool(mockFn);
258
+ const result = await tool.execute({ command: 'ready' }, createContext());
259
+ assert.strictEqual(result.metadata?.command, 'ready');
260
+ assert.deepStrictEqual(result.metadata?.args, ['ready']);
261
+ });
262
+ });
263
+ });
264
+ //# sourceMappingURL=beads.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"beads.test.js","sourceRoot":"","sources":["../../../src/tools/cli/beads.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * EditFile Tool
3
+ *
4
+ * Replaces an exact string occurrence in a file.
5
+ * Uses atomic write (write to temp file, then rename) to prevent
6
+ * data loss on failure. Validates paths against traversal and
7
+ * injection attacks using the shared path validation utilities.
8
+ */
9
+ import type { Tool } from '../interface.js';
10
+ /**
11
+ * EditFile tool implementation.
12
+ *
13
+ * Reads a file, replaces exactly one occurrence of oldString with newString,
14
+ * and writes the result back atomically (write temp + rename).
15
+ */
16
+ export declare const editFileTool: Tool;
17
+ //# sourceMappingURL=editFile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editFile.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/editFile.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAwB5C;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,IA8G1B,CAAC"}
@@ -0,0 +1,125 @@
1
+ /**
2
+ * EditFile Tool
3
+ *
4
+ * Replaces an exact string occurrence in a file.
5
+ * Uses atomic write (write to temp file, then rename) to prevent
6
+ * data loss on failure. Validates paths against traversal and
7
+ * injection attacks using the shared path validation utilities.
8
+ */
9
+ import { readFile, writeFile, rename } from 'node:fs/promises';
10
+ import { dirname, join } from 'node:path';
11
+ import { randomBytes } from 'node:crypto';
12
+ import { validateFilePath } from './readFile.js';
13
+ import { ToolError } from '../types.js';
14
+ /** Input schema for the editFile tool */
15
+ const inputSchema = {
16
+ type: 'object',
17
+ properties: {
18
+ filePath: {
19
+ type: 'string',
20
+ description: 'Absolute path to the file to edit',
21
+ },
22
+ oldString: {
23
+ type: 'string',
24
+ description: 'Exact string to find and replace (must match exactly once)',
25
+ },
26
+ newString: {
27
+ type: 'string',
28
+ description: 'Replacement string',
29
+ },
30
+ },
31
+ required: ['filePath', 'oldString', 'newString'],
32
+ };
33
+ /**
34
+ * EditFile tool implementation.
35
+ *
36
+ * Reads a file, replaces exactly one occurrence of oldString with newString,
37
+ * and writes the result back atomically (write temp + rename).
38
+ */
39
+ export const editFileTool = {
40
+ name: 'editFile',
41
+ description: 'Replace an exact string in a file. The oldString must match exactly once.',
42
+ inputSchema,
43
+ async execute(input, context) {
44
+ // Check permission
45
+ if (!context.permissions.allowFileWrite) {
46
+ throw new ToolError('File write permission denied', 'PERMISSION_DENIED', 'editFile');
47
+ }
48
+ // Validate path
49
+ const resolvedPath = validateFilePath(input.filePath, 'editFile');
50
+ // Validate oldString / newString
51
+ const oldString = input.oldString;
52
+ const newString = input.newString;
53
+ if (typeof oldString !== 'string') {
54
+ throw new ToolError('oldString is required and must be a string', 'INVALID_INPUT', 'editFile');
55
+ }
56
+ if (typeof newString !== 'string') {
57
+ throw new ToolError('newString is required and must be a string', 'INVALID_INPUT', 'editFile');
58
+ }
59
+ if (oldString.length === 0) {
60
+ throw new ToolError('oldString cannot be empty', 'INVALID_INPUT', 'editFile');
61
+ }
62
+ // Read file
63
+ let contents;
64
+ try {
65
+ contents = await readFile(resolvedPath, 'utf-8');
66
+ }
67
+ catch (err) {
68
+ const nodeErr = err;
69
+ if (nodeErr.code === 'ENOENT') {
70
+ throw new ToolError(`File not found: ${resolvedPath}`, 'NOT_FOUND', 'editFile', { cause: nodeErr });
71
+ }
72
+ if (nodeErr.code === 'EACCES') {
73
+ throw new ToolError(`Permission denied: ${resolvedPath}`, 'PERMISSION_DENIED', 'editFile', { cause: nodeErr });
74
+ }
75
+ throw new ToolError(`Failed to read file: ${nodeErr.message}`, 'EXECUTION_FAILED', 'editFile', { cause: nodeErr });
76
+ }
77
+ // Count occurrences of oldString
78
+ let count = 0;
79
+ let searchStart = 0;
80
+ while (true) {
81
+ const idx = contents.indexOf(oldString, searchStart);
82
+ if (idx === -1)
83
+ break;
84
+ count++;
85
+ searchStart = idx + oldString.length;
86
+ }
87
+ if (count === 0) {
88
+ throw new ToolError('oldString not found in file. Ensure the string matches exactly, including whitespace and indentation.', 'EXECUTION_FAILED', 'editFile');
89
+ }
90
+ if (count > 1) {
91
+ throw new ToolError(`oldString found ${count} times — ambiguous match, include more context to match exactly once`, 'EXECUTION_FAILED', 'editFile');
92
+ }
93
+ // Replace (exactly one occurrence)
94
+ const updated = contents.replace(oldString, newString);
95
+ // Atomic write: write to temp file, then rename
96
+ const dir = dirname(resolvedPath);
97
+ const tempSuffix = randomBytes(8).toString('hex');
98
+ const tempPath = join(dir, `.beth-edit-${tempSuffix}.tmp`);
99
+ try {
100
+ await writeFile(tempPath, updated, 'utf-8');
101
+ await rename(tempPath, resolvedPath);
102
+ }
103
+ catch (err) {
104
+ // Best-effort cleanup of temp file on failure
105
+ try {
106
+ const { unlink } = await import('node:fs/promises');
107
+ await unlink(tempPath);
108
+ }
109
+ catch {
110
+ // Ignore cleanup errors
111
+ }
112
+ const nodeErr = err;
113
+ throw new ToolError(`Failed to write file: ${nodeErr.message}`, 'EXECUTION_FAILED', 'editFile', { cause: nodeErr });
114
+ }
115
+ return {
116
+ success: true,
117
+ output: `Successfully replaced string in ${resolvedPath}`,
118
+ metadata: {
119
+ filePath: resolvedPath,
120
+ replacements: 1,
121
+ },
122
+ };
123
+ },
124
+ };
125
+ //# sourceMappingURL=editFile.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editFile.js","sourceRoot":"","sources":["../../../src/tools/cli/editFile.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ /**
2
+ * EditFile Tool Tests
3
+ *
4
+ * Tests for the editFile tool implementation.
5
+ * Uses node:test and node:assert with real file I/O via tmpdir.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=editFile.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editFile.test.d.ts","sourceRoot":"","sources":["../../../src/tools/cli/editFile.test.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}