atabey-mcp 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.
Files changed (44) hide show
  1. package/{src/constants.ts → dist/constants.js} +3 -17
  2. package/{src/index.ts → dist/index.js} +31 -59
  3. package/{src/resources/index.ts → dist/resources/index.js} +4 -11
  4. package/dist/src/cli/adapters/core.js +12 -19
  5. package/dist/src/shared/constants.js +1 -0
  6. package/{src/tools/control_plane/locking.ts → dist/tools/control_plane/locking.js} +11 -18
  7. package/{src/tools/control_plane/registry.ts → dist/tools/control_plane/registry.js} +5 -9
  8. package/{src/tools/definitions.ts → dist/tools/definitions.js} +2 -4
  9. package/{src/tools/file_system/batch_surgical_edit.ts → dist/tools/file_system/batch_surgical_edit.js} +9 -32
  10. package/{src/tools/file_system/patch_file.ts → dist/tools/file_system/patch_file.js} +2 -14
  11. package/{src/tools/file_system/read_file.ts → dist/tools/file_system/read_file.js} +6 -13
  12. package/{src/tools/file_system/replace_text.ts → dist/tools/file_system/replace_text.js} +6 -17
  13. package/{src/tools/file_system/write_file.ts → dist/tools/file_system/write_file.js} +5 -14
  14. package/{src/tools/framework/audit_deps.ts → dist/tools/framework/audit_deps.js} +8 -16
  15. package/{src/tools/framework/get_status.ts → dist/tools/framework/get_status.js} +1 -3
  16. package/{src/tools/framework/orchestrate.ts → dist/tools/framework/orchestrate.js} +1 -3
  17. package/{src/tools/framework/run_tests.ts → dist/tools/framework/run_tests.js} +8 -11
  18. package/{src/tools/framework/submit_plan.ts → dist/tools/framework/submit_plan.js} +1 -14
  19. package/{src/tools/framework/update_contract_hash.ts → dist/tools/framework/update_contract_hash.js} +1 -3
  20. package/{src/tools/framework/update_memory.ts → dist/tools/framework/update_memory.js} +1 -3
  21. package/{src/tools/index.ts → dist/tools/index.js} +3 -7
  22. package/{src/tools/memory/get_insights.ts → dist/tools/memory/get_insights.js} +3 -10
  23. package/{src/tools/memory/read_memory.ts → dist/tools/memory/read_memory.js} +3 -6
  24. package/{src/tools/messaging/log_action.ts → dist/tools/messaging/log_action.js} +1 -7
  25. package/{src/tools/messaging/send_message.ts → dist/tools/messaging/send_message.js} +14 -17
  26. package/{src/tools/observability/check_ports.ts → dist/tools/observability/check_ports.js} +6 -10
  27. package/{src/tools/observability/get_health.ts → dist/tools/observability/get_health.js} +1 -6
  28. package/{src/tools/quality/check_lint.ts → dist/tools/quality/check_lint.js} +1 -7
  29. package/{src/tools/search/get_gaps.ts → dist/tools/search/get_gaps.js} +12 -18
  30. package/{src/tools/search/get_map.ts → dist/tools/search/get_map.js} +14 -19
  31. package/{src/tools/search/grep_search.ts → dist/tools/search/grep_search.js} +23 -23
  32. package/{src/tools/search/list_dir.ts → dist/tools/search/list_dir.js} +4 -10
  33. package/{src/tools/shell/run_command.ts → dist/tools/shell/run_command.js} +1 -11
  34. package/dist/tools/types.js +1 -0
  35. package/{src/utils/cli.ts → dist/utils/cli.js} +25 -19
  36. package/{src/utils/compliance.ts → dist/utils/compliance.js} +34 -74
  37. package/{src/utils/fs.ts → dist/utils/fs.js} +9 -10
  38. package/{src/utils/metrics.ts → dist/utils/metrics.js} +11 -28
  39. package/{src/utils/permissions.ts → dist/utils/permissions.js} +11 -28
  40. package/{src/utils/security.ts → dist/utils/security.js} +9 -15
  41. package/package.json +7 -1
  42. package/src/declarations.d.ts +0 -19
  43. package/src/tools/types.ts +0 -89
  44. package/tsconfig.json +0 -13
@@ -1,12 +1,9 @@
1
1
  import path from "path";
2
-
3
2
  /**
4
3
  * Agent Atabey — Single Source of Truth for framework constants.
5
4
  * Import from here instead of hardcoding paths, phases, or directory names.
6
5
  */
7
-
8
6
  // ─── Framework identity ───────────────────────────────────────────────────
9
-
10
7
  export const FRAMEWORK = {
11
8
  NAME: "Agent Atabey",
12
9
  CORE_DIR: ".atabey",
@@ -17,7 +14,6 @@ export const FRAMEWORK = {
17
14
  // This is where all skills are stored
18
15
  SKILLS_DIR: "skills",
19
16
  };
20
-
21
17
  export const FRAMEWORK_SUBDIRS = {
22
18
  AGENTS: "agents",
23
19
  SKILLS: "skills",
@@ -28,26 +24,22 @@ export const FRAMEWORK_SUBDIRS = {
28
24
  LOGS: "logs",
29
25
  CONFIG: "config",
30
26
  };
31
-
32
27
  export const ROOT_CONFIG_FILES = {
33
28
  MCP: "mcp.json",
34
29
  NATIVE_MODULES: "native-modules.json",
35
30
  TSCONFIG: "tsconfig.json",
36
31
  ESLINT: "eslint.config.js",
37
32
  };
38
-
39
33
  export const MCP = {
40
34
  // Environment variable used by MCP to identify project root
41
35
  PROJECT_ROOT_ENV: "ATABEY_PROJECT_ROOT",
42
36
  // Environment variable for test mode
43
37
  TEST_DIR_ENV: "ATABEY_TEST_DIR",
44
38
  };
45
-
46
39
  export const MEMORY_FILES = {
47
40
  STATE: "state.json",
48
41
  SHARED_FACTS: "shared_facts.json",
49
42
  };
50
-
51
43
  export const NATIVE_AGENT_PATHS = {
52
44
  gemini: ".gemini/agents",
53
45
  claude: ".claude/agents",
@@ -56,23 +48,17 @@ export const NATIVE_AGENT_PATHS = {
56
48
  grok: ".grok",
57
49
  "antigravity-cli": ".agents/agents",
58
50
  };
59
-
60
51
  // ─── Backward-compatible aliases ──────────────────────────────────────────
61
-
62
52
  export const CORE_FRAMEWORK_DIR = FRAMEWORK.CORE_DIR;
63
53
  export const UNIFIED_HUB_DIR = FRAMEWORK.UNIFIED_HUB_DIR;
64
54
  export const SKILLS_HUB_PATH = pathJoin(UNIFIED_HUB_DIR, FRAMEWORK_SUBDIRS.SKILLS);
65
-
66
55
  // ─── Path Helpers ─────────────────────────────────────────────────────────
67
-
68
- function pathJoin(...args: string[]): string {
56
+ function pathJoin(...args) {
69
57
  return path.join(...args);
70
58
  }
71
-
72
- function corePath(subdir: string, filename: string): string {
59
+ function corePath(subdir, filename) {
73
60
  return pathJoin(FRAMEWORK.CORE_DIR, subdir, filename);
74
61
  }
75
-
76
- export function knowledgePath(filename: string): string {
62
+ export function knowledgePath(filename) {
77
63
  return corePath(FRAMEWORK_SUBDIRS.KNOWLEDGE, filename);
78
64
  }
@@ -4,55 +4,39 @@ import path from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
6
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
- import {
8
- CallToolRequestSchema,
9
- ListToolsRequestSchema,
10
- ListResourcesRequestSchema,
11
- ReadResourceRequestSchema,
12
- } from "@modelcontextprotocol/sdk/types.js";
13
-
14
-
7
+ import { CallToolRequestSchema, ListToolsRequestSchema, ListResourcesRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
15
8
  import { TOOLS, toolHandlers } from "./tools/index.js";
16
9
  import { RESOURCES, handleReadResource } from "./resources/index.js";
17
-
18
-
19
10
  // ─── Server Setup ─────────────────────────────────────────────────
20
-
21
11
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
22
-
23
12
  // Robustly find package.json by walking up from __dirname
24
- function findPackageJson(startDir: string): string {
13
+ function findPackageJson(startDir) {
25
14
  let currentDir = startDir;
26
15
  while (currentDir !== path.parse(currentDir).root) {
27
16
  const pkgPath = path.join(currentDir, "package.json");
28
- if (fs.existsSync(pkgPath)) return pkgPath;
17
+ if (fs.existsSync(pkgPath))
18
+ return pkgPath;
29
19
  currentDir = path.dirname(currentDir);
30
20
  }
31
21
  throw new Error("Could not find package.json for atabey-mcp");
32
22
  }
33
-
34
23
  const pkgPath = findPackageJson(__dirname);
35
24
  const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
36
25
  const serverVersion = pkg.version;
37
-
38
- const server = new Server(
39
- {
40
- name: "atabey-mcp",
41
- version: serverVersion,
26
+ const server = new Server({
27
+ name: "atabey-mcp",
28
+ version: serverVersion,
29
+ }, {
30
+ capabilities: {
31
+ tools: {},
32
+ resources: {},
42
33
  },
43
- {
44
- capabilities: {
45
- tools: {},
46
- resources: {},
47
- },
48
- }
49
- );
50
-
34
+ });
51
35
  // Basic Schema Validator for Required Fields
52
- function validateArgs(toolName: string, args: Record<string, unknown>): string | null {
36
+ function validateArgs(toolName, args) {
53
37
  const definition = TOOLS.find(t => t.name === toolName);
54
- if (!definition) return `Unknown tool: ${toolName}`;
55
-
38
+ if (!definition)
39
+ return `Unknown tool: ${toolName}`;
56
40
  const required = definition.inputSchema.required || [];
57
41
  for (const field of required) {
58
42
  if (args[field] === undefined || args[field] === null || args[field] === "") {
@@ -61,22 +45,19 @@ function validateArgs(toolName: string, args: Record<string, unknown>): string |
61
45
  }
62
46
  return null;
63
47
  }
64
-
65
48
  server.setRequestHandler(ListToolsRequestSchema, async (request) => {
66
49
  // 2026 Stateless Spec: Log client info from metadata if available
67
- const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
50
+ const meta = request._meta;
68
51
  if (meta) {
69
52
  process.stderr.write(`[MCP] Stateless ListTools from ${meta.client?.name || "unknown"} v${meta.client?.version || "?.?"}\n`);
70
53
  }
71
54
  return { tools: TOOLS };
72
55
  });
73
-
74
56
  server.setRequestHandler(ListResourcesRequestSchema, async () => {
75
57
  return { resources: RESOURCES };
76
58
  });
77
-
78
59
  server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
79
- const uri = (request as { params: { uri: string } }).params.uri;
60
+ const uri = request.params.uri;
80
61
  try {
81
62
  const content = await handleReadResource(uri);
82
63
  return {
@@ -88,84 +69,75 @@ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
88
69
  },
89
70
  ],
90
71
  };
91
- } catch (error: unknown) {
72
+ }
73
+ catch (error) {
92
74
  const message = error instanceof Error ? error.message : String(error);
93
75
  throw new Error(`Failed to read resource: ${message}`, { cause: error });
94
76
  }
95
77
  });
96
-
97
78
  server.setRequestHandler(CallToolRequestSchema, async (request) => {
98
- const req = request as { params: { name: string, arguments?: Record<string, unknown> } };
79
+ const req = request;
99
80
  const { name, arguments: args } = req.params;
100
- const meta = (request as { _meta?: { client?: { name?: string; version?: string } } })._meta;
101
-
81
+ const meta = request._meta;
102
82
  // 2026 Stateless Spec: Prioritize metadata-driven context
103
83
  if (meta) {
104
84
  process.stderr.write(`[MCP] Stateless CallTool: ${name} (Client: ${meta.client?.name || "unknown"})\n`);
105
85
  }
106
-
107
86
  const projectRoot = process.env.ATABEY_PROJECT_ROOT || process.cwd();
108
-
109
87
  try {
110
88
  const handler = toolHandlers[name];
111
89
  if (!handler) {
112
90
  return {
113
91
  isError: true,
114
- content: [{ type: "text" as const, text: `[ERROR] Unknown tool: ${name}` }],
92
+ content: [{ type: "text", text: `[ERROR] Unknown tool: ${name}` }],
115
93
  };
116
94
  }
117
-
118
95
  // [SECURITY] Runtime Validation
119
96
  const validationError = validateArgs(name, args || {});
120
97
  if (validationError) {
121
98
  return {
122
99
  isError: true,
123
- content: [{ type: "text" as const, text: `[ERROR] Validation Error: ${validationError}` }],
100
+ content: [{ type: "text", text: `[ERROR] Validation Error: ${validationError}` }],
124
101
  };
125
102
  }
126
-
127
103
  return await handler(projectRoot, args || {});
128
- } catch (error: unknown) {
104
+ }
105
+ catch (error) {
129
106
  const message = error instanceof Error ? error.message : "Unknown error occurred";
130
107
  return {
131
108
  isError: true,
132
- content: [{ type: "text" as const, text: `[ERROR] Execution failed: ${message}` }],
109
+ content: [{ type: "text", text: `[ERROR] Execution failed: ${message}` }],
133
110
  };
134
111
  }
135
112
  });
136
-
137
113
  // ─── Graceful Startup & Shutdown ──────────────────────────────────
138
-
139
114
  async function run() {
140
115
  const transport = new StdioServerTransport();
141
-
142
116
  // Prevent unhandled errors from crashing the MCP stream
143
- process.on("uncaughtException", (error: Error) => {
117
+ process.on("uncaughtException", (error) => {
144
118
  process.stderr.write(`[atabey-mcp] Uncaught exception: ${error.message}
145
119
  `);
146
120
  });
147
- process.on("unhandledRejection", (reason: unknown) => {
121
+ process.on("unhandledRejection", (reason) => {
148
122
  const message = reason instanceof Error ? reason.message : String(reason);
149
123
  process.stderr.write(`[atabey-mcp] Unhandled rejection: ${message}
150
124
  `);
151
125
  });
152
-
153
126
  // Graceful shutdown on SIGINT/SIGTERM
154
127
  const shutdown = async () => {
155
128
  try {
156
129
  await server.close();
157
- } catch {
130
+ }
131
+ catch {
158
132
  // Already closed or failed — safe to ignore
159
133
  }
160
134
  process.exit(0);
161
135
  };
162
136
  process.on("SIGINT", shutdown);
163
137
  process.on("SIGTERM", shutdown);
164
-
165
138
  await server.connect(transport);
166
139
  }
167
-
168
- run().catch((error: Error) => {
140
+ run().catch((error) => {
169
141
  process.stderr.write(`[atabey-mcp] Fatal startup error: ${error.message}
170
142
  `);
171
143
  process.exit(1);
@@ -1,5 +1,4 @@
1
- import { Storage, AgentRow, TaskRow } from "../../../src/shared/storage.js";
2
-
1
+ import { Storage } from "../../../src/shared/storage.js";
3
2
  /**
4
3
  * [DATA] MCP Resource Definitions
5
4
  */
@@ -23,43 +22,37 @@ export const RESOURCES = [
23
22
  mimeType: "text/markdown"
24
23
  }
25
24
  ];
26
-
27
- export async function handleReadResource(uri: string): Promise<string> {
25
+ export async function handleReadResource(uri) {
28
26
  if (uri === "atabey://army/status") {
29
27
  const agents = Storage.getAllAgents();
30
28
  let md = "# [AI] Agent Army Status\n\n| Agent | State | Active Task | Last Updated |\n| :--- | :--- | :--- | :--- |\n";
31
- agents.forEach((a: AgentRow) => {
29
+ agents.forEach((a) => {
32
30
  md += `| @${a.name} | ${a.state} | ${a.task} | ${a.last_updated} |\n`;
33
31
  });
34
32
  return md;
35
33
  }
36
-
37
34
  if (uri === "atabey://plan/active") {
38
35
  const tasks = Storage.getTasks();
39
36
  let md = "# 📋 Active Execution Plan\n\n| ID | Task | Agent | Status | Dependencies |\n| :--- | :--- | :--- | :--- | :--- |\n";
40
- tasks.forEach((t: TaskRow) => {
37
+ tasks.forEach((t) => {
41
38
  const deps = t.dependencies.join(", ") || "-";
42
39
  md += `| ${t.id} | ${t.description} | ${t.agent} | ${t.status} | ${deps} |\n`;
43
40
  });
44
41
  return md;
45
42
  }
46
-
47
43
  if (uri === "atabey://memory/project") {
48
44
  const fs = await import("fs");
49
45
  const path = await import("path");
50
46
  const { getFrameworkDir } = await import("../../../src/cli/utils/memory.js");
51
-
52
47
  const projectRoot = process.env.ATABEY_PROJECT_ROOT || process.cwd();
53
48
  const fwDir = getFrameworkDir();
54
49
  const p = path.isAbsolute(fwDir)
55
50
  ? path.join(fwDir, "memory", "PROJECT_MEMORY.md")
56
51
  : path.join(projectRoot, fwDir, "memory", "PROJECT_MEMORY.md");
57
-
58
52
  if (fs.existsSync(p)) {
59
53
  return fs.readFileSync(p, "utf8");
60
54
  }
61
55
  return "Project memory not found. Run 'atabey init' first.";
62
56
  }
63
-
64
57
  throw new Error(`Unknown resource URI: ${uri}`);
65
58
  }
@@ -2,6 +2,7 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { writeJsonFile } from "../utils/fs.js";
4
4
  import { getPackageRoot } from "../utils/pkg.js";
5
+ import { MCP } from "../../shared/constants.js";
5
6
  import { ADAPTER_CONFIGS, POST_INIT_HANDLERS } from "../../modules/adapters/definitions.js";
6
7
  export const ADAPTERS = ADAPTER_CONFIGS;
7
8
  export const SHIM_FILES = Object.keys(ADAPTERS).map((id) => ADAPTERS[id].shimFile);
@@ -25,7 +26,7 @@ export function buildMcpServerEntry(projectRoot) {
25
26
  let relativePath;
26
27
  if (isLocalFrameworkDev) {
27
28
  // In local framework dev, always use the build path directly relative to project root
28
- let mcpServerPath = path.join(packageRoot, "framework-mcp/dist/index.js");
29
+ let mcpServerPath = path.join(packageRoot, MCP.SERVER_DIST_PATH);
29
30
  if (!fs.existsSync(mcpServerPath)) {
30
31
  mcpServerPath = path.join(packageRoot, "../atabey-mcp/dist/index.js");
31
32
  }
@@ -39,39 +40,31 @@ export function buildMcpServerEntry(projectRoot) {
39
40
  }
40
41
  else {
41
42
  // If we are initializing in a user's project:
42
- // We want to target the local node_modules of the user's project.
43
- // Even if node_modules is not populated yet (e.g. running via npx before npm install),
44
- // we should write the local node_modules path so that it will work once installed.
45
- const localAtabeyPath = path.join(projectRoot, "node_modules/atabey/framework-mcp/dist/index.js");
46
- const localScopedPath = path.join(projectRoot, "node_modules/atabey-mcp/dist/index.js");
47
- if (fs.existsSync(localScopedPath)) {
48
- relativePath = path.relative(projectRoot, localScopedPath);
49
- }
50
- else if (fs.existsSync(localAtabeyPath)) {
51
- relativePath = path.relative(projectRoot, localAtabeyPath);
52
- }
53
- else {
54
- // Default to the standard relative local path in node_modules/atabey
55
- // This is clean and portable across machines/developers.
56
- relativePath = "node_modules/atabey/framework-mcp/dist/index.js";
43
+ // We target the atabey-mcp package which is a dependency of atabey.
44
+ // This ensures a stable path across different npm/pnpm/yarn setups.
45
+ relativePath = "node_modules/atabey-mcp/dist/index.js";
46
+ // Fallback check if it actually exists in a different location during init
47
+ const localAtabeyPath = path.join(projectRoot, "node_modules/atabey", MCP.SERVER_DIST_PATH);
48
+ if (!fs.existsSync(path.join(projectRoot, relativePath)) && fs.existsSync(localAtabeyPath)) {
49
+ relativePath = path.join("node_modules/atabey", MCP.SERVER_DIST_PATH);
57
50
  }
58
51
  }
59
52
  return {
60
53
  command: "node",
61
54
  args: [relativePath],
62
55
  env: {
63
- ATABEY_PROJECT_ROOT: projectRoot,
56
+ [MCP.PROJECT_ROOT_ENV]: projectRoot,
64
57
  },
65
58
  };
66
59
  }
67
60
  export function runAdapterPostInit(adapter, projectRoot) {
68
61
  const mcpEntry = buildMcpServerEntry(projectRoot);
69
- const mcpBlock = { mcpServers: { "atabey": mcpEntry } };
62
+ const mcpBlock = { mcpServers: { [MCP.SERVER_NAME]: mcpEntry } };
70
63
  const postInitFn = POST_INIT_HANDLERS[adapter.id];
71
64
  if (postInitFn) {
72
65
  postInitFn(projectRoot, mcpBlock);
73
66
  }
74
- const rootMcpPath = path.join(projectRoot, "mcp.json");
67
+ const rootMcpPath = path.join(projectRoot, MCP.ROOT_CONFIG_FILE);
75
68
  if (!fs.existsSync(rootMcpPath)) {
76
69
  writeJsonFile(rootMcpPath, mcpBlock);
77
70
  }
@@ -143,6 +143,7 @@ export const MCP = {
143
143
  ROOT_CONFIG_FILE: "mcp.json",
144
144
  PROJECT_ROOT_ENV: "ATABEY_PROJECT_ROOT",
145
145
  TEST_DIR_ENV: "ATABEY_TEST_DIR",
146
+ SERVER_DIST_PATH: "framework-mcp/dist/index.js",
146
147
  };
147
148
  export const ROOT_CONFIG_FILES = {
148
149
  MCP: MCP.ROOT_CONFIG_FILE,
@@ -1,26 +1,22 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { ToolResult, AcquireLockArgs, ReleaseLockArgs } from "../types.js";
4
3
  import { resolveFrameworkDir } from "../../utils/security.js";
5
-
6
4
  /**
7
5
  * Handles acquiring a stateful lock on a resource.
8
6
  */
9
- export async function handleAcquireLock(projectRoot: string, args: AcquireLockArgs): Promise<ToolResult> {
7
+ export async function handleAcquireLock(projectRoot, args) {
10
8
  const { resource, agent, ttl = 60 } = args;
11
9
  const frameworkDir = resolveFrameworkDir(projectRoot);
12
10
  const lockDir = path.join(projectRoot, frameworkDir, "locks");
13
11
  const lockPath = path.join(lockDir, `${resource}.lock`);
14
-
15
12
  try {
16
- if (!fs.existsSync(lockDir)) fs.mkdirSync(lockDir, { recursive: true });
17
-
13
+ if (!fs.existsSync(lockDir))
14
+ fs.mkdirSync(lockDir, { recursive: true });
18
15
  // Check for stale lock first
19
16
  if (fs.existsSync(lockPath)) {
20
17
  const stat = fs.statSync(lockPath);
21
18
  const now = new Date().getTime();
22
19
  const age = (now - stat.mtimeMs) / 1000;
23
-
24
20
  if (age < ttl) {
25
21
  return {
26
22
  isError: true,
@@ -32,21 +28,21 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
32
28
  try {
33
29
  fs.renameSync(lockPath, tempLockPath);
34
30
  fs.unlinkSync(tempLockPath);
35
- } catch {
31
+ }
32
+ catch {
36
33
  // If rename failed, it means another agent already evicting/deleting it.
37
34
  // Do not delete anything else; proceed and let writeFileSync (wx flag) fail if a new lock exists.
38
35
  }
39
36
  }
40
-
41
37
  // Use 'wx' flag for atomic file creation
42
38
  const lockData = JSON.stringify({ agent, timestamp: new Date().toISOString() });
43
39
  fs.writeFileSync(lockPath, lockData, { flag: "wx" });
44
-
45
40
  return {
46
41
  content: [{ type: "text", text: `[OK] Lock acquired for resource '${resource}' by ${agent}.` }]
47
42
  };
48
- } catch (e) {
49
- const error = e as { code?: string };
43
+ }
44
+ catch (e) {
45
+ const error = e;
50
46
  if (error.code === "EEXIST") {
51
47
  return {
52
48
  isError: true,
@@ -59,20 +55,17 @@ export async function handleAcquireLock(projectRoot: string, args: AcquireLockAr
59
55
  };
60
56
  }
61
57
  }
62
-
63
58
  /**
64
59
  * Handles releasing a lock.
65
60
  */
66
- export async function handleReleaseLock(projectRoot: string, args: ReleaseLockArgs): Promise<ToolResult> {
61
+ export async function handleReleaseLock(projectRoot, args) {
67
62
  const { resource, agent } = args;
68
63
  const frameworkDir = resolveFrameworkDir(projectRoot);
69
64
  const lockPath = path.join(projectRoot, frameworkDir, "locks", `${resource}.lock`);
70
-
71
65
  try {
72
66
  if (!fs.existsSync(lockPath)) {
73
67
  return { content: [{ type: "text", text: `ℹ️ No lock found for resource '${resource}'.` }] };
74
68
  }
75
-
76
69
  const lockData = JSON.parse(fs.readFileSync(lockPath, "utf8"));
77
70
  if (lockData.agent !== agent) {
78
71
  return {
@@ -80,10 +73,10 @@ export async function handleReleaseLock(projectRoot: string, args: ReleaseLockAr
80
73
  content: [{ type: "text", text: `[ERROR] Denied: You do not own the lock for '${resource}'. Owned by ${lockData.agent}.` }]
81
74
  };
82
75
  }
83
-
84
76
  fs.unlinkSync(lockPath);
85
77
  return { content: [{ type: "text", text: `[OK] Lock released for resource '${resource}' by ${agent}.` }] };
86
- } catch (e) {
78
+ }
79
+ catch (e) {
87
80
  return { isError: true, content: [{ type: "text", text: `Failed to release lock: ${String(e)}` }] };
88
81
  }
89
82
  }
@@ -1,21 +1,18 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
- import { ToolResult, RegisterAgentArgs } from "../types.js";
4
3
  import { resolveFrameworkDir } from "../../utils/security.js";
5
-
6
4
  /**
7
5
  * Handles agent registration with the Control Plane.
8
6
  * This can be used to validate permissions and active status.
9
7
  */
10
- export async function handleRegisterAgent(projectRoot: string, args: RegisterAgentArgs): Promise<ToolResult> {
8
+ export async function handleRegisterAgent(projectRoot, args) {
11
9
  const { agent, role, capability = 5, specialties } = args;
12
10
  const frameworkDir = resolveFrameworkDir(projectRoot);
13
11
  const registryDir = path.join(projectRoot, frameworkDir, "registry");
14
12
  const agentFile = path.join(registryDir, `${agent.replace("@", "")}_active.json`);
15
-
16
13
  try {
17
- if (!fs.existsSync(registryDir)) fs.mkdirSync(registryDir, { recursive: true });
18
-
14
+ if (!fs.existsSync(registryDir))
15
+ fs.mkdirSync(registryDir, { recursive: true });
19
16
  const agentData = {
20
17
  agent,
21
18
  role,
@@ -24,13 +21,12 @@ export async function handleRegisterAgent(projectRoot: string, args: RegisterAge
24
21
  last_seen: new Date().toISOString(),
25
22
  status: "ACTIVE"
26
23
  };
27
-
28
24
  fs.writeFileSync(agentFile, JSON.stringify(agentData, null, 2));
29
-
30
25
  return {
31
26
  content: [{ type: "text", text: `[ATABEY] Agent ${agent} (${role}) registered successfully in the Atabey Control Plane.` }]
32
27
  };
33
- } catch (e) {
28
+ }
29
+ catch (e) {
34
30
  return {
35
31
  isError: true,
36
32
  content: [{ type: "text", text: `Failed to register agent: ${String(e)}` }]
@@ -1,6 +1,4 @@
1
- import { ToolDefinition } from "../tools/types.js";
2
-
3
- export const TOOLS: ToolDefinition[] = [
1
+ export const TOOLS = [
4
2
  {
5
3
  name: "read_file",
6
4
  description: "Read the content of a file within the project. Supports optional line range reading to prevent stream overload.",
@@ -321,4 +319,4 @@ export const TOOLS: ToolDefinition[] = [
321
319
  required: ["path"],
322
320
  },
323
321
  }
324
- ];
322
+ ];
@@ -1,53 +1,37 @@
1
1
  import fs from "fs";
2
2
  import { safePath } from "../../utils/security.js";
3
3
  import { writeTextFileAtomic } from "../../utils/fs.js";
4
- import { BatchSurgicalEditArgs, ToolResult } from "../types.js";
5
4
  import { Metrics } from "../../utils/metrics.js";
6
5
  import { verifyCorporateCompliance, verifyRiskAndAwaitApproval } from "../../utils/compliance.js";
7
6
  import { verifyWritePermission } from "../../utils/permissions.js";
8
-
9
- interface SurgicalEdit {
10
- path: string;
11
- oldText: string;
12
- newText: string;
13
- allowMultiple?: boolean;
14
- }
15
-
16
7
  /**
17
8
  * Performs multiple surgical text replacements across multiple files in a single batch.
18
9
  */
19
- export async function handleBatchSurgicalEdit(projectRoot: string, args: BatchSurgicalEditArgs): Promise<ToolResult> {
20
- const edits = args.edits as SurgicalEdit[];
10
+ export async function handleBatchSurgicalEdit(projectRoot, args) {
11
+ const edits = args.edits;
21
12
  if (!Array.isArray(edits) || edits.length === 0) {
22
13
  const err = "No edits provided in the batch request.";
23
14
  Metrics.logError(projectRoot, "@mcp", "batch_surgical_edit", err);
24
15
  throw new Error(err);
25
16
  }
26
-
27
- const results: string[] = [];
17
+ const results = [];
28
18
  let totalTokens = 0;
29
-
30
19
  for (const edit of edits) {
31
20
  const filePath = safePath(projectRoot, edit.path);
32
-
33
21
  // ENFORCE PERMISSION MATRIX
34
22
  verifyWritePermission(projectRoot, edit.path);
35
-
36
23
  if (!fs.existsSync(filePath)) {
37
24
  const err = `File not found: ${edit.path}`;
38
25
  Metrics.logError(projectRoot, "@mcp", `batch_surgical_edit:${edit.path}`, err);
39
26
  throw new Error(err);
40
27
  }
41
-
42
28
  const content = fs.readFileSync(filePath, "utf8");
43
29
  const { oldText, newText, allowMultiple = false } = edit;
44
-
45
30
  if (!content.includes(oldText)) {
46
31
  const err = `Text not found in file ${edit.path}`;
47
32
  Metrics.logError(projectRoot, "@mcp", `batch_surgical_edit:${edit.path}`, err);
48
33
  throw new Error(err);
49
34
  }
50
-
51
35
  // Surgical precision guard
52
36
  if (!allowMultiple) {
53
37
  const firstIndex = content.indexOf(oldText);
@@ -58,30 +42,23 @@ export async function handleBatchSurgicalEdit(projectRoot: string, args: BatchSu
58
42
  throw new Error(err);
59
43
  }
60
44
  }
61
-
62
- const newContent = allowMultiple
45
+ const newContent = allowMultiple
63
46
  ? content.replace(new RegExp(oldText.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g"), newText)
64
47
  : content.replace(oldText, newText);
65
-
66
48
  // ENFORCE CORPORATE COMPLIANCE
67
49
  verifyCorporateCompliance(newContent, edit.path);
68
-
69
50
  // ENFORCE RISK & HUMAN APPROVAL GATEWAY
70
51
  await verifyRiskAndAwaitApproval(projectRoot, newContent, edit.path);
71
-
72
52
  writeTextFileAtomic(filePath, newContent);
73
-
74
53
  const tokens = Metrics.estimateTokens(newText);
75
54
  totalTokens += tokens;
76
55
  results.push(`[OK] Edited ${edit.path}`);
77
56
  }
78
-
79
57
  Metrics.logUsage(projectRoot, "@mcp", `batch_surgical_edit: ${edits.length} files`, totalTokens);
80
-
81
- return {
82
- content: [{
83
- type: "text",
84
- text: `Successfully performed ${edits.length} edits:\n${results.join("\n")}`
85
- }]
58
+ return {
59
+ content: [{
60
+ type: "text",
61
+ text: `Successfully performed ${edits.length} edits:\n${results.join("\n")}`
62
+ }]
86
63
  };
87
64
  }