@perstack/base 0.0.7 → 0.0.8

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/dist/index.js CHANGED
@@ -1,53 +1,56 @@
1
- #!/usr/bin/env node
1
+ import { realpathSync, existsSync, statSync } from 'fs';
2
+ import fs, { appendFile, readFile, mkdir, unlink, writeFile, readdir, rename, stat } from 'fs/promises';
3
+ import os from 'os';
4
+ import path, { dirname, extname, basename, resolve, join } from 'path';
5
+ import mime from 'mime-types';
6
+ import { dedent } from 'ts-dedent';
7
+ import { z } from 'zod';
8
+ import { execFile } from 'child_process';
9
+ import { promisify } from 'util';
2
10
 
3
- // src/index.ts
4
- import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
- import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
- import { Command } from "commander";
7
-
8
- // package.json
9
- var package_default = {
10
- name: "@perstack/base",
11
- version: "0.0.7",
12
- description: "Perstack base skills for agents.",
13
- author: "Wintermute Technologies, Inc.",
14
- type: "module",
15
- bin: {
16
- base: "dist/index.js"
17
- },
18
- exports: {
19
- ".": "./dist/index.js"
20
- },
21
- files: [
22
- "dist"
23
- ],
24
- scripts: {
25
- clean: "rm -rf dist",
26
- dev: "tsup --watch --config ../../tsup.config.ts",
27
- build: "npm run clean && tsup --config ../../tsup.config.ts",
28
- prepublishOnly: "npm run clean && npm run build"
29
- },
30
- dependencies: {
31
- "@modelcontextprotocol/sdk": "^1.17.1",
32
- commander: "^14.0.0",
33
- "mime-types": "^3.0.1",
34
- "ts-dedent": "^2.2.0",
35
- zod: "^3.23.8"
36
- },
37
- devDependencies: {
38
- "@tsconfig/node22": "^22.0.2",
39
- "@types/mime-types": "^3.0.1",
40
- "@types/node": "^24.2.0",
41
- tsup: "^8.5.0",
42
- typescript: "^5.9.2"
43
- },
44
- engines: {
45
- node: ">=22.0.0"
11
+ // src/lib/path.ts
12
+ var workspacePath = realpathSync(expandHome(process.cwd()));
13
+ function expandHome(filepath) {
14
+ if (filepath.startsWith("~/") || filepath === "~") {
15
+ return path.join(os.homedir(), filepath.slice(1));
46
16
  }
47
- };
48
-
49
- // src/lib/workspace-api.ts
50
- import mime from "mime-types";
17
+ return filepath;
18
+ }
19
+ async function validatePath(requestedPath) {
20
+ const expandedPath = expandHome(requestedPath);
21
+ const absolute = path.isAbsolute(expandedPath) ? path.resolve(expandedPath) : path.resolve(process.cwd(), expandedPath);
22
+ if (absolute === `${workspacePath}/perstack`) {
23
+ throw new Error("Access denied - perstack directory is not allowed");
24
+ }
25
+ try {
26
+ const realAbsolute = await fs.realpath(absolute);
27
+ if (!realAbsolute.startsWith(workspacePath)) {
28
+ throw new Error("Access denied - symlink target outside allowed directories");
29
+ }
30
+ return realAbsolute;
31
+ } catch (error) {
32
+ const parentDir = path.dirname(absolute);
33
+ try {
34
+ const realParentPath = await fs.realpath(parentDir);
35
+ if (!realParentPath.startsWith(workspacePath)) {
36
+ throw new Error("Access denied - parent directory outside allowed directories");
37
+ }
38
+ return absolute;
39
+ } catch {
40
+ if (!absolute.startsWith(workspacePath)) {
41
+ throw new Error(
42
+ `Access denied - path outside allowed directories: ${absolute} not in ${workspacePath}`
43
+ );
44
+ }
45
+ throw new Error(`Parent directory does not exist: ${parentDir}`);
46
+ }
47
+ }
48
+ }
49
+ var WorkspaceMode = /* @__PURE__ */ ((WorkspaceMode2) => {
50
+ WorkspaceMode2["LOCAL"] = "local";
51
+ WorkspaceMode2["STUDIO"] = "studio";
52
+ return WorkspaceMode2;
53
+ })(WorkspaceMode || {});
51
54
  var getWorkspaceApiConfig = () => {
52
55
  const baseUrl = process.env.PERSTACK_API_BASE_URL;
53
56
  const apiKey = process.env.PERSTACK_API_KEY;
@@ -199,6 +202,16 @@ var deleteWorkspaceItem = async (workspaceItemId) => {
199
202
  throw new Error(`Failed to delete workspace item: ${response.status} ${errorText}`);
200
203
  }
201
204
  };
205
+ var getWorkspaceItemContent = async (workspaceItem) => {
206
+ const config = getWorkspaceApiConfig();
207
+ if (!config) {
208
+ throw new Error("Workspace API not configured");
209
+ }
210
+ if (workspaceItem.type !== "workspaceItemFile") {
211
+ throw new Error("Can only get content for files");
212
+ }
213
+ throw new Error("Getting file content is not implemented yet");
214
+ };
202
215
 
203
216
  // src/lib/register-tool.ts
204
217
  function resolveToolByMode(localModeFunction, studioModeFunction) {
@@ -218,58 +231,6 @@ function resolveToolByMode(localModeFunction, studioModeFunction) {
218
231
  }
219
232
  };
220
233
  }
221
-
222
- // src/tools/append-text-file.ts
223
- import { existsSync, statSync } from "fs";
224
- import { appendFile } from "fs/promises";
225
- import { readFile } from "fs/promises";
226
- import { dedent } from "ts-dedent";
227
- import { z } from "zod";
228
-
229
- // src/lib/path.ts
230
- import { realpathSync } from "fs";
231
- import fs from "fs/promises";
232
- import os from "os";
233
- import path from "path";
234
- var workspacePath = realpathSync(expandHome(process.cwd()));
235
- function expandHome(filepath) {
236
- if (filepath.startsWith("~/") || filepath === "~") {
237
- return path.join(os.homedir(), filepath.slice(1));
238
- }
239
- return filepath;
240
- }
241
- async function validatePath(requestedPath) {
242
- const expandedPath = expandHome(requestedPath);
243
- const absolute = path.isAbsolute(expandedPath) ? path.resolve(expandedPath) : path.resolve(process.cwd(), expandedPath);
244
- if (absolute === `${workspacePath}/perstack`) {
245
- throw new Error("Access denied - perstack directory is not allowed");
246
- }
247
- try {
248
- const realAbsolute = await fs.realpath(absolute);
249
- if (!realAbsolute.startsWith(workspacePath)) {
250
- throw new Error("Access denied - symlink target outside allowed directories");
251
- }
252
- return realAbsolute;
253
- } catch (error) {
254
- const parentDir = path.dirname(absolute);
255
- try {
256
- const realParentPath = await fs.realpath(parentDir);
257
- if (!realParentPath.startsWith(workspacePath)) {
258
- throw new Error("Access denied - parent directory outside allowed directories");
259
- }
260
- return absolute;
261
- } catch {
262
- if (!absolute.startsWith(workspacePath)) {
263
- throw new Error(
264
- `Access denied - path outside allowed directories: ${absolute} not in ${workspacePath}`
265
- );
266
- }
267
- throw new Error(`Parent directory does not exist: ${parentDir}`);
268
- }
269
- }
270
- }
271
-
272
- // src/tools/append-text-file.ts
273
234
  var inputSchema = z.object({
274
235
  path: z.string().describe("Target file path to append to."),
275
236
  text: z.string().min(1).max(2e3).describe("Text to append to the file. Max 2000 characters.")
@@ -333,21 +294,16 @@ async function appendTextFileStudioMode(input) {
333
294
  {
334
295
  type: "workspaceItemFile",
335
296
  path: relativePath,
336
- owner: "expert",
337
- lifecycle: "expertJob",
338
297
  permission: "readWrite"
339
298
  },
340
299
  content
341
300
  );
342
301
  return { path: relativePath, text };
343
302
  }
344
-
345
- // src/tools/attempt-completion.ts
346
- import { dedent as dedent2 } from "ts-dedent";
347
303
  function attemptCompletionConfig() {
348
304
  return {
349
305
  title: "Attempt completion",
350
- description: dedent2`
306
+ description: dedent`
351
307
  Task completion signal that triggers immediate final report generation.
352
308
  Use cases:
353
309
  - Signaling task completion to Perstack runtime
@@ -371,20 +327,13 @@ async function attemptCompletion() {
371
327
  message: "End the agent loop and provide a final report"
372
328
  };
373
329
  }
374
-
375
- // src/tools/create-directory.ts
376
- import { existsSync as existsSync2, statSync as statSync2 } from "fs";
377
- import { mkdir } from "fs/promises";
378
- import { dirname } from "path";
379
- import { dedent as dedent3 } from "ts-dedent";
380
- import { z as z2 } from "zod";
381
- var inputSchema2 = z2.object({
382
- path: z2.string()
330
+ var inputSchema2 = z.object({
331
+ path: z.string()
383
332
  });
384
333
  function createDirectoryConfig() {
385
334
  return {
386
335
  title: "Create directory",
387
- description: dedent3`
336
+ description: dedent`
388
337
  Directory creator for establishing folder structures in the workspace.
389
338
 
390
339
  Use cases:
@@ -408,13 +357,13 @@ function createDirectoryConfig() {
408
357
  async function createDirectoryLocalMode(input) {
409
358
  const { path: path2 } = input;
410
359
  const validatedPath = await validatePath(path2);
411
- const exists = existsSync2(validatedPath);
360
+ const exists = existsSync(validatedPath);
412
361
  if (exists) {
413
362
  throw new Error(`Directory ${path2} already exists`);
414
363
  }
415
364
  const parentDir = dirname(validatedPath);
416
- if (existsSync2(parentDir)) {
417
- const parentStats = statSync2(parentDir);
365
+ if (existsSync(parentDir)) {
366
+ const parentStats = statSync(parentDir);
418
367
  if (!(parentStats.mode & 128)) {
419
368
  throw new Error(`Parent directory ${parentDir} is not writable`);
420
369
  }
@@ -441,8 +390,6 @@ async function createDirectoryStudioMode(input) {
441
390
  await createWorkspaceItem({
442
391
  type: "workspaceItemDirectory",
443
392
  path: currentPath,
444
- owner: "expert",
445
- lifecycle: "expertJob",
446
393
  permission: "readWrite"
447
394
  });
448
395
  }
@@ -452,19 +399,13 @@ async function createDirectoryStudioMode(input) {
452
399
  path: relativePath
453
400
  };
454
401
  }
455
-
456
- // src/tools/delete-file.ts
457
- import { existsSync as existsSync3, statSync as statSync3 } from "fs";
458
- import { unlink } from "fs/promises";
459
- import { dedent as dedent4 } from "ts-dedent";
460
- import { z as z3 } from "zod";
461
- var inputSchema3 = z3.object({
462
- path: z3.string()
402
+ var inputSchema3 = z.object({
403
+ path: z.string()
463
404
  });
464
405
  function deleteFileConfig() {
465
406
  return {
466
407
  title: "Delete file",
467
- description: dedent4`
408
+ description: dedent`
468
409
  File deleter for removing files from the workspace.
469
410
 
470
411
  Use cases:
@@ -488,10 +429,10 @@ function deleteFileConfig() {
488
429
  async function deleteFileLocalMode(input) {
489
430
  const { path: path2 } = input;
490
431
  const validatedPath = await validatePath(path2);
491
- if (!existsSync3(validatedPath)) {
432
+ if (!existsSync(validatedPath)) {
492
433
  throw new Error(`File ${path2} does not exist.`);
493
434
  }
494
- const stats = statSync3(validatedPath);
435
+ const stats = statSync(validatedPath);
495
436
  if (stats.isDirectory()) {
496
437
  throw new Error(`Path ${path2} is a directory. Use delete directory tool instead.`);
497
438
  }
@@ -520,21 +461,15 @@ async function deleteFileStudioMode(input) {
520
461
  path: relativePath
521
462
  };
522
463
  }
523
-
524
- // src/tools/edit-text-file.ts
525
- import { existsSync as existsSync4, statSync as statSync4 } from "fs";
526
- import { readFile as readFile2, writeFile } from "fs/promises";
527
- import { dedent as dedent5 } from "ts-dedent";
528
- import { z as z4 } from "zod";
529
- var inputSchema4 = z4.object({
530
- path: z4.string().describe("Target file path to edit."),
531
- newText: z4.string().min(1).max(2e3).describe("Text to append to the file. Max 2000 characters."),
532
- oldText: z4.string().min(1).max(2e3).describe("Exact text to find and replace. Max 2000 characters.")
464
+ var inputSchema4 = z.object({
465
+ path: z.string().describe("Target file path to edit."),
466
+ newText: z.string().min(1).max(2e3).describe("Text to append to the file. Max 2000 characters."),
467
+ oldText: z.string().min(1).max(2e3).describe("Exact text to find and replace. Max 2000 characters.")
533
468
  });
534
469
  function editTextFileConfig() {
535
470
  return {
536
471
  title: "Edit text file",
537
- description: dedent5`
472
+ description: dedent`
538
473
  Text file editor for modifying existing files with precise text replacement.
539
474
 
540
475
  Use cases:
@@ -562,10 +497,10 @@ function editTextFileConfig() {
562
497
  async function editTextFileLocalMode(input) {
563
498
  const { path: path2, newText, oldText } = input;
564
499
  const validatedPath = await validatePath(path2);
565
- if (!existsSync4(validatedPath)) {
500
+ if (!existsSync(validatedPath)) {
566
501
  throw new Error(`File ${path2} does not exist.`);
567
502
  }
568
- const stats = statSync4(validatedPath);
503
+ const stats = statSync(validatedPath);
569
504
  if (!(stats.mode & 128)) {
570
505
  throw new Error(`File ${path2} is not writable`);
571
506
  }
@@ -589,13 +524,11 @@ async function editTextFileStudioMode(input) {
589
524
  }
590
525
  await editTextFileLocalMode(input);
591
526
  await deleteWorkspaceItem(existingItem.id);
592
- const content = await readFile2(validatedPath, "utf-8");
527
+ const content = await readFile(validatedPath, "utf-8");
593
528
  await createWorkspaceItem(
594
529
  {
595
530
  type: "workspaceItemFile",
596
531
  path: relativePath,
597
- owner: "expert",
598
- lifecycle: "expertJob",
599
532
  permission: "readWrite"
600
533
  },
601
534
  content
@@ -610,7 +543,7 @@ function normalizeLineEndings(text) {
610
543
  return text.replace(/\r\n/g, "\n");
611
544
  }
612
545
  async function applyFileEdit(filePath, newText, oldText) {
613
- const content = normalizeLineEndings(await readFile2(filePath, "utf-8"));
546
+ const content = normalizeLineEndings(await readFile(filePath, "utf-8"));
614
547
  const normalizedOld = normalizeLineEndings(oldText);
615
548
  const normalizedNew = normalizeLineEndings(newText);
616
549
  if (!content.includes(normalizedOld)) {
@@ -619,20 +552,117 @@ async function applyFileEdit(filePath, newText, oldText) {
619
552
  const modifiedContent = content.replace(normalizedOld, normalizedNew);
620
553
  await writeFile(filePath, modifiedContent, "utf-8");
621
554
  }
555
+ var execFileAsync = promisify(execFile);
556
+ var execInputSchema = z.object({
557
+ command: z.string().describe("The command to execute"),
558
+ args: z.array(z.string()).describe("The arguments to pass to the command"),
559
+ env: z.record(z.string(), z.string()).describe("The environment variables to set"),
560
+ cwd: z.string().describe("The working directory to execute the command in"),
561
+ stdout: z.boolean().describe("Whether to capture the standard output"),
562
+ stderr: z.boolean().describe("Whether to capture the standard error"),
563
+ timeout: z.number().optional().describe("Timeout in milliseconds")
564
+ });
565
+ function execConfig() {
566
+ return {
567
+ title: "Execute Command",
568
+ description: dedent`
569
+ A tool for executing a command.
570
+ This tool helps execute a command with the given parameters.
571
+
572
+ When to use this tool:
573
+ - When you need to run system tasks or scripts.
574
+ - When automating the use of command-line tools or utilities.
575
+
576
+ Key features:
577
+ - Flexible Execution Environment: Specify environment variables, working directory, and arguments to tailor the command execution.
578
+ - Output Capture: Control whether to capture the standard output and standard error from the executed command.
579
+ - Simple Interface: A unified input schema consolidates all parameters needed for command execution.
622
580
 
623
- // src/tools/get-file-info.ts
624
- import { existsSync as existsSync5, statSync as statSync5 } from "fs";
625
- import { basename, dirname as dirname2, extname, resolve } from "path";
626
- import mime2 from "mime-types";
627
- import { dedent as dedent6 } from "ts-dedent";
628
- import { z as z5 } from "zod";
629
- var inputSchema5 = z5.object({
630
- path: z5.string()
581
+ Parameters explained:
582
+ - command: The command to execute (e.g., \`ls\`, \`python\`).
583
+ - args: A list of arguments to pass to the command, functioning similarly to command-line input.
584
+ - env: A record of key-value pairs representing environment variables for the command's execution environment.
585
+ - cwd: The working directory in which the command will run; both relative and absolute paths are supported.
586
+ - stdout: A boolean flag indicating whether to capture the standard output (set to \`true\` to capture).
587
+ - stderr: A boolean flag indicating whether to capture the standard error (set to \`true\` to capture).
588
+ - timeout: The timeout in milliseconds for the command execution.
589
+
590
+ Rules:
591
+ - Safety: Only execute commands from trusted sources to avoid executing malicious code.
592
+ - Command Termination: Do not execute commands that remain running in the foreground (e.g., \`tail -f\`), as the tool requires commands to complete.
593
+ - Resource Management: Be cautious when running commands that may consume significant system resources or produce large amounts of output.
594
+ - Error Handling: Implement proper error handling to catch and manage any issues that arise during command execution.
595
+ - Parameter Validation: Ensure that all provided parameters adhere to the expected format to prevent unexpected behavior.
596
+ `,
597
+ inputSchema: execInputSchema.shape
598
+ };
599
+ }
600
+ async function exec(input) {
601
+ try {
602
+ const { stdout, stderr } = await execFileAsync(input.command, input.args, {
603
+ cwd: input.cwd,
604
+ env: { ...process.env, ...input.env },
605
+ timeout: input.timeout
606
+ });
607
+ let output = "";
608
+ if (input.stdout) {
609
+ output += stdout;
610
+ }
611
+ if (input.stderr) {
612
+ output += stderr;
613
+ }
614
+ if (!output.trim()) {
615
+ output = "Command executed successfully, but produced no output.";
616
+ }
617
+ return {
618
+ content: [
619
+ {
620
+ type: "text",
621
+ text: output
622
+ }
623
+ ]
624
+ };
625
+ } catch (error) {
626
+ let errMsg = "";
627
+ const execError = error;
628
+ if (execError && (execError.killed || execError.signal === "SIGTERM") && typeof input.timeout === "number") {
629
+ errMsg = `Error: Command timed out after ${input.timeout}ms.
630
+ `;
631
+ } else if (error instanceof Error) {
632
+ if (error.message.includes("timeout")) {
633
+ errMsg = `Error: Command timed out after ${input.timeout}ms.
634
+ `;
635
+ } else {
636
+ errMsg = `Error: ${error.message}
637
+ `;
638
+ }
639
+ } else {
640
+ errMsg = "An unknown error occurred.\n";
641
+ }
642
+ if (execError.stdout && input.stdout) {
643
+ errMsg += `Standard Output: ${execError.stdout}`;
644
+ }
645
+ if (execError.stderr && input.stderr) {
646
+ errMsg += `
647
+ Standard Error: ${execError.stderr}`;
648
+ }
649
+ return {
650
+ content: [
651
+ {
652
+ type: "text",
653
+ text: errMsg
654
+ }
655
+ ]
656
+ };
657
+ }
658
+ }
659
+ var inputSchema5 = z.object({
660
+ path: z.string()
631
661
  });
632
662
  function getFileInfoConfig() {
633
663
  return {
634
664
  title: "Get file info",
635
- description: dedent6`
665
+ description: dedent`
636
666
  File information retriever for detailed metadata about files and directories.
637
667
 
638
668
  Use cases:
@@ -656,12 +686,12 @@ function getFileInfoConfig() {
656
686
  async function getFileInfoLocalMode(input) {
657
687
  const { path: path2 } = input;
658
688
  const validatedPath = await validatePath(path2);
659
- if (!existsSync5(validatedPath)) {
689
+ if (!existsSync(validatedPath)) {
660
690
  throw new Error(`File or directory ${path2} does not exist`);
661
691
  }
662
- const stats = statSync5(validatedPath);
692
+ const stats = statSync(validatedPath);
663
693
  const isDirectory = stats.isDirectory();
664
- const mimeType = isDirectory ? null : mime2.lookup(validatedPath) || "application/octet-stream";
694
+ const mimeType = isDirectory ? null : mime.lookup(validatedPath) || "application/octet-stream";
665
695
  const formatSize = (bytes) => {
666
696
  if (bytes === 0) return "0 B";
667
697
  const units = ["B", "KB", "MB", "GB", "TB"];
@@ -673,7 +703,7 @@ async function getFileInfoLocalMode(input) {
673
703
  path: validatedPath,
674
704
  absolutePath: resolve(validatedPath),
675
705
  name: basename(validatedPath),
676
- directory: dirname2(validatedPath),
706
+ directory: dirname(validatedPath),
677
707
  extension: isDirectory ? null : extname(validatedPath),
678
708
  type: isDirectory ? "directory" : "file",
679
709
  mimeType,
@@ -714,20 +744,13 @@ async function getFileInfoStudioMode(input) {
714
744
  } : null
715
745
  };
716
746
  }
717
-
718
- // src/tools/list-directory.ts
719
- import { existsSync as existsSync6, statSync as statSync6 } from "fs";
720
- import { readdir } from "fs/promises";
721
- import { join } from "path";
722
- import { dedent as dedent7 } from "ts-dedent";
723
- import { z as z6 } from "zod";
724
- var inputSchema6 = z6.object({
725
- path: z6.string()
747
+ var inputSchema6 = z.object({
748
+ path: z.string()
726
749
  });
727
750
  function listDirectoryConfig() {
728
751
  return {
729
752
  title: "List directory",
730
- description: dedent7`
753
+ description: dedent`
731
754
  Directory content lister with detailed file information.
732
755
 
733
756
  Use cases:
@@ -751,10 +774,10 @@ function listDirectoryConfig() {
751
774
  async function listDirectoryLocalMode(input) {
752
775
  const { path: path2 } = input;
753
776
  const validatedPath = await validatePath(path2);
754
- if (!existsSync6(validatedPath)) {
777
+ if (!existsSync(validatedPath)) {
755
778
  throw new Error(`Directory ${path2} does not exist.`);
756
779
  }
757
- const stats = statSync6(validatedPath);
780
+ const stats = statSync(validatedPath);
758
781
  if (!stats.isDirectory()) {
759
782
  throw new Error(`Path ${path2} is not a directory.`);
760
783
  }
@@ -763,7 +786,7 @@ async function listDirectoryLocalMode(input) {
763
786
  for (const entry of entries.sort()) {
764
787
  try {
765
788
  const fullPath = await validatePath(join(validatedPath, entry));
766
- const entryStats = statSync6(fullPath);
789
+ const entryStats = statSync(fullPath);
767
790
  const item = {
768
791
  name: entry,
769
792
  path: entry,
@@ -807,21 +830,14 @@ async function listDirectoryStudioMode(input) {
807
830
  }))
808
831
  };
809
832
  }
810
-
811
- // src/tools/move-file.ts
812
- import { existsSync as existsSync7, statSync as statSync7 } from "fs";
813
- import { mkdir as mkdir2, rename } from "fs/promises";
814
- import { dirname as dirname3 } from "path";
815
- import { dedent as dedent8 } from "ts-dedent";
816
- import { z as z7 } from "zod";
817
- var inputSchema7 = z7.object({
818
- source: z7.string(),
819
- destination: z7.string()
833
+ var inputSchema7 = z.object({
834
+ source: z.string(),
835
+ destination: z.string()
820
836
  });
821
837
  function moveFileConfig() {
822
838
  return {
823
839
  title: "Move file",
824
- description: dedent8`
840
+ description: dedent`
825
841
  File mover for relocating or renaming files within the workspace.
826
842
 
827
843
  Use cases:
@@ -847,18 +863,18 @@ async function moveFileLocalMode(input) {
847
863
  const { source, destination } = input;
848
864
  const validatedSource = await validatePath(source);
849
865
  const validatedDestination = await validatePath(destination);
850
- if (!existsSync7(validatedSource)) {
866
+ if (!existsSync(validatedSource)) {
851
867
  throw new Error(`Source file ${source} does not exist.`);
852
868
  }
853
- const sourceStats = statSync7(validatedSource);
869
+ const sourceStats = statSync(validatedSource);
854
870
  if (!(sourceStats.mode & 128)) {
855
871
  throw new Error(`Source file ${source} is not writable`);
856
872
  }
857
- if (existsSync7(validatedDestination)) {
873
+ if (existsSync(validatedDestination)) {
858
874
  throw new Error(`Destination ${destination} already exists.`);
859
875
  }
860
- const destDir = dirname3(validatedDestination);
861
- await mkdir2(destDir, { recursive: true });
876
+ const destDir = dirname(validatedDestination);
877
+ await mkdir(destDir, { recursive: true });
862
878
  await rename(validatedSource, validatedDestination);
863
879
  return {
864
880
  source: validatedSource,
@@ -879,15 +895,13 @@ async function moveFileStudioMode(input) {
879
895
  if (destItem) {
880
896
  throw new Error(`Destination ${relativeDestination} already exists.`);
881
897
  }
882
- const destDir = dirname3(relativeDestination);
898
+ const destDir = dirname(relativeDestination);
883
899
  if (destDir !== "." && destDir !== "/") {
884
900
  const destDirItem = await findWorkspaceItem(destDir);
885
901
  if (!destDirItem) {
886
902
  await createWorkspaceItem({
887
903
  type: "workspaceItemDirectory",
888
904
  path: destDir,
889
- owner: "expert",
890
- lifecycle: "expertJob",
891
905
  permission: "readWrite"
892
906
  });
893
907
  }
@@ -901,21 +915,14 @@ async function moveFileStudioMode(input) {
901
915
  destination: relativeDestination
902
916
  };
903
917
  }
904
-
905
- // src/tools/read-image-file.ts
906
- import { existsSync as existsSync8 } from "fs";
907
- import { stat } from "fs/promises";
908
- import mime3 from "mime-types";
909
- import { dedent as dedent9 } from "ts-dedent";
910
- import { z as z8 } from "zod";
911
918
  var MAX_IMAGE_SIZE = 15 * 1024 * 1024;
912
- var inputSchema8 = z8.object({
913
- path: z8.string()
919
+ var inputSchema8 = z.object({
920
+ path: z.string()
914
921
  });
915
922
  function readImageFileConfig() {
916
923
  return {
917
924
  title: "Read image file",
918
- description: dedent9`
925
+ description: dedent`
919
926
  Image file reader that converts images to base64 encoded strings with MIME type validation.
920
927
 
921
928
  Use cases:
@@ -944,11 +951,11 @@ function readImageFileConfig() {
944
951
  async function readImageFile(input) {
945
952
  const { path: path2 } = input;
946
953
  const validatedPath = await validatePath(path2);
947
- const isFile = existsSync8(validatedPath);
954
+ const isFile = existsSync(validatedPath);
948
955
  if (!isFile) {
949
956
  throw new Error(`File ${path2} does not exist.`);
950
957
  }
951
- const mimeType = mime3.lookup(validatedPath);
958
+ const mimeType = mime.lookup(validatedPath);
952
959
  if (!mimeType || !["image/png", "image/jpeg", "image/gif", "image/webp"].includes(mimeType)) {
953
960
  throw new Error(`File ${path2} is not supported.`);
954
961
  }
@@ -965,21 +972,14 @@ async function readImageFile(input) {
965
972
  size: fileStats.size
966
973
  };
967
974
  }
968
-
969
- // src/tools/read-pdf-file.ts
970
- import { existsSync as existsSync9 } from "fs";
971
- import { stat as stat2 } from "fs/promises";
972
- import mime4 from "mime-types";
973
- import { dedent as dedent10 } from "ts-dedent";
974
- import { z as z9 } from "zod";
975
975
  var MAX_PDF_SIZE = 30 * 1024 * 1024;
976
- var inputSchema9 = z9.object({
977
- path: z9.string()
976
+ var inputSchema9 = z.object({
977
+ path: z.string()
978
978
  });
979
979
  function readPdfFileConfig() {
980
980
  return {
981
981
  title: "Read PDF file",
982
- description: dedent10`
982
+ description: dedent`
983
983
  PDF file reader that converts documents to base64 encoded resources.
984
984
 
985
985
  Use cases:
@@ -1004,15 +1004,15 @@ function readPdfFileConfig() {
1004
1004
  async function readPdfFile(input) {
1005
1005
  const { path: path2 } = input;
1006
1006
  const validatedPath = await validatePath(path2);
1007
- const isFile = existsSync9(validatedPath);
1007
+ const isFile = existsSync(validatedPath);
1008
1008
  if (!isFile) {
1009
1009
  throw new Error(`File ${path2} does not exist.`);
1010
1010
  }
1011
- const mimeType = mime4.lookup(validatedPath);
1011
+ const mimeType = mime.lookup(validatedPath);
1012
1012
  if (mimeType !== "application/pdf") {
1013
1013
  throw new Error(`File ${path2} is not a PDF file.`);
1014
1014
  }
1015
- const fileStats = await stat2(validatedPath);
1015
+ const fileStats = await stat(validatedPath);
1016
1016
  const fileSizeMB = fileStats.size / (1024 * 1024);
1017
1017
  if (fileStats.size > MAX_PDF_SIZE) {
1018
1018
  throw new Error(
@@ -1025,21 +1025,15 @@ async function readPdfFile(input) {
1025
1025
  size: fileStats.size
1026
1026
  };
1027
1027
  }
1028
-
1029
- // src/tools/read-text-file.ts
1030
- import { existsSync as existsSync10 } from "fs";
1031
- import { readFile as readFile3 } from "fs/promises";
1032
- import { dedent as dedent11 } from "ts-dedent";
1033
- import { z as z10 } from "zod";
1034
- var inputSchema10 = z10.object({
1035
- path: z10.string(),
1036
- from: z10.number().optional().describe("The line number to start reading from."),
1037
- to: z10.number().optional().describe("The line number to stop reading at.")
1028
+ var inputSchema10 = z.object({
1029
+ path: z.string(),
1030
+ from: z.number().optional().describe("The line number to start reading from."),
1031
+ to: z.number().optional().describe("The line number to stop reading at.")
1038
1032
  });
1039
1033
  function readTextFileConfig() {
1040
1034
  return {
1041
1035
  title: "Read text file",
1042
- description: dedent11`
1036
+ description: dedent`
1043
1037
  Text file reader with line range support for UTF-8 encoded files.
1044
1038
 
1045
1039
  Use cases:
@@ -1066,11 +1060,11 @@ function readTextFileConfig() {
1066
1060
  async function readTextFile(input) {
1067
1061
  const { path: path2, from, to } = input;
1068
1062
  const validatedPath = await validatePath(path2);
1069
- const isFile = existsSync10(validatedPath);
1063
+ const isFile = existsSync(validatedPath);
1070
1064
  if (!isFile) {
1071
1065
  throw new Error(`File ${path2} does not exist.`);
1072
1066
  }
1073
- const fileContent = await readFile3(validatedPath, "utf-8");
1067
+ const fileContent = await readFile(validatedPath, "utf-8");
1074
1068
  const lines = fileContent.split("\n");
1075
1069
  const fromLine = from ?? 0;
1076
1070
  const toLine = to ?? lines.length;
@@ -1083,17 +1077,13 @@ async function readTextFile(input) {
1083
1077
  to: toLine
1084
1078
  };
1085
1079
  }
1086
-
1087
- // src/tools/test-url.ts
1088
- import { dedent as dedent12 } from "ts-dedent";
1089
- import { z as z11 } from "zod";
1090
- var inputSchema11 = z11.object({
1091
- urls: z11.array(z11.string()).min(1).max(10).describe("Array of URLs to test (max 10 URLs).")
1080
+ var inputSchema11 = z.object({
1081
+ urls: z.array(z.string()).min(1).max(10).describe("Array of URLs to test (max 10 URLs).")
1092
1082
  });
1093
1083
  function testUrlConfig() {
1094
1084
  return {
1095
1085
  title: "Test URL",
1096
- description: dedent12`
1086
+ description: dedent`
1097
1087
  URL tester that validates multiple URLs and extracts metadata.
1098
1088
 
1099
1089
  Use cases:
@@ -1191,18 +1181,14 @@ function extractDescription(html) {
1191
1181
  }
1192
1182
  return "";
1193
1183
  }
1194
-
1195
- // src/tools/think.ts
1196
- import { dedent as dedent13 } from "ts-dedent";
1197
- import { z as z12 } from "zod";
1198
- var inputSchema12 = z12.object({
1199
- thought: z12.string().describe("Your current thinking step"),
1200
- nextThoughtNeeded: z12.boolean().optional().describe("true if you need more thinking, even if at what seemed like the end")
1184
+ var inputSchema12 = z.object({
1185
+ thought: z.string().describe("Your current thinking step"),
1186
+ nextThoughtNeeded: z.boolean().optional().describe("true if you need more thinking, even if at what seemed like the end")
1201
1187
  });
1202
1188
  function thinkConfig() {
1203
1189
  return {
1204
1190
  title: "think",
1205
- description: dedent13`
1191
+ description: dedent`
1206
1192
  Sequential thinking tool for step-by-step problem analysis and solution development.
1207
1193
 
1208
1194
  Use cases:
@@ -1247,10 +1233,6 @@ var thought = new Thought();
1247
1233
  async function think(input) {
1248
1234
  return thought.processThought(input);
1249
1235
  }
1250
-
1251
- // src/tools/todo.ts
1252
- import { dedent as dedent14 } from "ts-dedent";
1253
- import { z as z13 } from "zod";
1254
1236
  var Todo = class {
1255
1237
  currentTodoId = 0;
1256
1238
  todos = [];
@@ -1280,14 +1262,14 @@ var Todo = class {
1280
1262
  }
1281
1263
  };
1282
1264
  var todoSingleton = new Todo();
1283
- var todoInputSchema = z13.object({
1284
- newTodos: z13.array(z13.string()).describe("New todos to add").optional(),
1285
- completedTodos: z13.array(z13.number()).describe("Todo ids that are completed").optional()
1265
+ var todoInputSchema = z.object({
1266
+ newTodos: z.array(z.string()).describe("New todos to add").optional(),
1267
+ completedTodos: z.array(z.number()).describe("Todo ids that are completed").optional()
1286
1268
  });
1287
1269
  function todoConfig() {
1288
1270
  return {
1289
1271
  title: "todo",
1290
- description: dedent14`
1272
+ description: dedent`
1291
1273
  Todo list manager that tracks tasks and their completion status.
1292
1274
 
1293
1275
  Use cases:
@@ -1310,11 +1292,11 @@ function todoConfig() {
1310
1292
  async function todo(input) {
1311
1293
  return todoSingleton.processTodo(input);
1312
1294
  }
1313
- var clearTodoInputSchema = z13.object({});
1295
+ var clearTodoInputSchema = z.object({});
1314
1296
  function clearTodoConfig() {
1315
1297
  return {
1316
1298
  title: "clearTodo",
1317
- description: dedent14`
1299
+ description: dedent`
1318
1300
  Clears the todo list.
1319
1301
 
1320
1302
  Use cases:
@@ -1332,22 +1314,14 @@ function clearTodoConfig() {
1332
1314
  async function clearTodo(input) {
1333
1315
  return todoSingleton.clearTodo();
1334
1316
  }
1335
-
1336
- // src/tools/write-text-file.ts
1337
- import { existsSync as existsSync11, statSync as statSync8 } from "fs";
1338
- import { writeFile as writeFile2 } from "fs/promises";
1339
- import { mkdir as mkdir3 } from "fs/promises";
1340
- import { dirname as dirname4 } from "path";
1341
- import { dedent as dedent15 } from "ts-dedent";
1342
- import { z as z14 } from "zod";
1343
- var toolInputSchema = z14.object({
1344
- path: z14.string().describe("Target file path (relative or absolute)."),
1345
- text: z14.string().min(1).max(1e4).describe("Text to write to the file. Max 10000 characters.")
1317
+ var toolInputSchema = z.object({
1318
+ path: z.string().describe("Target file path (relative or absolute)."),
1319
+ text: z.string().min(1).max(1e4).describe("Text to write to the file. Max 10000 characters.")
1346
1320
  });
1347
1321
  function writeTextFileConfig() {
1348
1322
  return {
1349
1323
  title: "writeTextFile",
1350
- description: dedent15`
1324
+ description: dedent`
1351
1325
  Text file writer that creates or overwrites files with UTF-8 content.
1352
1326
 
1353
1327
  Use cases:
@@ -1372,15 +1346,15 @@ function writeTextFileConfig() {
1372
1346
  async function writeTextFileLocalMode(input) {
1373
1347
  const { path: path2, text } = input;
1374
1348
  const validatedPath = await validatePath(path2);
1375
- if (existsSync11(validatedPath)) {
1376
- const stats = statSync8(validatedPath);
1349
+ if (existsSync(validatedPath)) {
1350
+ const stats = statSync(validatedPath);
1377
1351
  if (!(stats.mode & 128)) {
1378
1352
  throw new Error(`File ${path2} is not writable`);
1379
1353
  }
1380
1354
  }
1381
- const dir = dirname4(validatedPath);
1382
- await mkdir3(dir, { recursive: true });
1383
- await writeFile2(validatedPath, text, "utf-8");
1355
+ const dir = dirname(validatedPath);
1356
+ await mkdir(dir, { recursive: true });
1357
+ await writeFile(validatedPath, text, "utf-8");
1384
1358
  return {
1385
1359
  path: validatedPath,
1386
1360
  text
@@ -1406,8 +1380,6 @@ async function writeTextFileStudioMode(input) {
1406
1380
  await createWorkspaceItem({
1407
1381
  type: "workspaceItemDirectory",
1408
1382
  path: currentPath,
1409
- owner: "expert",
1410
- lifecycle: "expertJob",
1411
1383
  permission: "readWrite"
1412
1384
  });
1413
1385
  }
@@ -1416,8 +1388,6 @@ async function writeTextFileStudioMode(input) {
1416
1388
  {
1417
1389
  type: "workspaceItemFile",
1418
1390
  path: relativePath,
1419
- owner: "expert",
1420
- lifecycle: "expertJob",
1421
1391
  permission: "readWrite"
1422
1392
  },
1423
1393
  text
@@ -1429,77 +1399,6 @@ async function writeTextFileStudioMode(input) {
1429
1399
  };
1430
1400
  }
1431
1401
 
1432
- // src/index.ts
1433
- async function main() {
1434
- const program = new Command();
1435
- program.name(package_default.name).description(package_default.description).version(package_default.version, "-v, --version", "display the version number").action(async () => {
1436
- const server = new McpServer(
1437
- {
1438
- name: package_default.name,
1439
- version: package_default.version
1440
- },
1441
- {
1442
- capabilities: {
1443
- tools: {}
1444
- }
1445
- }
1446
- );
1447
- server.registerTool(
1448
- "attemptCompletion",
1449
- attemptCompletionConfig(),
1450
- resolveToolByMode(attemptCompletion)
1451
- );
1452
- server.registerTool("think", thinkConfig(), resolveToolByMode(think));
1453
- server.registerTool("todo", todoConfig(), resolveToolByMode(todo));
1454
- server.registerTool("clearTodo", clearTodoConfig(), resolveToolByMode(clearTodo));
1455
- server.registerTool("testUrl", testUrlConfig(), resolveToolByMode(testUrls));
1456
- server.registerTool(
1457
- "getFileInfo",
1458
- getFileInfoConfig(),
1459
- resolveToolByMode(getFileInfoLocalMode, getFileInfoStudioMode)
1460
- );
1461
- server.registerTool("readTextFile", readTextFileConfig(), resolveToolByMode(readTextFile));
1462
- server.registerTool("readImageFile", readImageFileConfig(), resolveToolByMode(readImageFile));
1463
- server.registerTool("readPdfFile", readPdfFileConfig(), resolveToolByMode(readPdfFile));
1464
- server.registerTool(
1465
- "writeTextFile",
1466
- writeTextFileConfig(),
1467
- resolveToolByMode(writeTextFileLocalMode, writeTextFileStudioMode)
1468
- );
1469
- server.registerTool(
1470
- "appendTextFile",
1471
- appendTextFileConfig(),
1472
- resolveToolByMode(appendTextFileLocalMode, appendTextFileStudioMode)
1473
- );
1474
- server.registerTool(
1475
- "editTextFile",
1476
- editTextFileConfig(),
1477
- resolveToolByMode(editTextFileLocalMode, editTextFileStudioMode)
1478
- );
1479
- server.registerTool(
1480
- "moveFile",
1481
- moveFileConfig(),
1482
- resolveToolByMode(moveFileLocalMode, moveFileStudioMode)
1483
- );
1484
- server.registerTool(
1485
- "deleteFile",
1486
- deleteFileConfig(),
1487
- resolveToolByMode(deleteFileLocalMode, deleteFileStudioMode)
1488
- );
1489
- server.registerTool(
1490
- "listDirectory",
1491
- listDirectoryConfig(),
1492
- resolveToolByMode(listDirectoryLocalMode, listDirectoryStudioMode)
1493
- );
1494
- server.registerTool(
1495
- "createDirectory",
1496
- createDirectoryConfig(),
1497
- resolveToolByMode(createDirectoryLocalMode, createDirectoryStudioMode)
1498
- );
1499
- const transport = new StdioServerTransport();
1500
- await server.connect(transport);
1501
- });
1502
- program.parse();
1503
- }
1504
- main();
1402
+ export { WorkspaceMode, appendTextFileConfig, appendTextFileLocalMode, appendTextFileStudioMode, attemptCompletion, attemptCompletionConfig, clearTodo, clearTodoConfig, createDirectoryConfig, createDirectoryLocalMode, createDirectoryStudioMode, createWorkspaceItem, deleteFileConfig, deleteFileLocalMode, deleteFileStudioMode, deleteWorkspaceItem, editTextFileConfig, editTextFileLocalMode, editTextFileStudioMode, exec, execConfig, findWorkspaceItem, getFileInfoConfig, getFileInfoLocalMode, getFileInfoStudioMode, getWorkspaceApiConfig, getWorkspaceItemContent, getWorkspaceItems, getWorkspaceMode, listDirectoryConfig, listDirectoryLocalMode, listDirectoryStudioMode, moveFileConfig, moveFileLocalMode, moveFileStudioMode, readImageFile, readImageFileConfig, readPdfFile, readPdfFileConfig, readTextFile, readTextFileConfig, resolveToolByMode, testUrlConfig, testUrls, think, thinkConfig, todo, todoConfig, updateWorkspaceItem, validatePath, workspacePath, writeTextFileConfig, writeTextFileLocalMode, writeTextFileStudioMode };
1403
+ //# sourceMappingURL=index.js.map
1505
1404
  //# sourceMappingURL=index.js.map