agent-enderun 1.11.2 → 1.11.4

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 (126) hide show
  1. package/README.md +5 -5
  2. package/bin/validate-agent-army.js +0 -1
  3. package/dist/framework-mcp/src/index.js +11 -1
  4. package/dist/framework-mcp/src/index.js.map +1 -1
  5. package/dist/framework-mcp/src/tools/definitions.js +0 -10
  6. package/dist/framework-mcp/src/tools/definitions.js.map +1 -1
  7. package/dist/framework-mcp/src/tools/index.js +0 -2
  8. package/dist/framework-mcp/src/tools/index.js.map +1 -1
  9. package/dist/framework-mcp/src/utils/compliance.d.ts +1 -1
  10. package/dist/framework-mcp/src/utils/compliance.js +68 -19
  11. package/dist/framework-mcp/src/utils/compliance.js.map +1 -1
  12. package/dist/src/cli/adapters/scaffold.d.ts +1 -1
  13. package/dist/src/cli/adapters/scaffold.js +8 -8
  14. package/dist/src/cli/adapters/scaffold.js.map +1 -1
  15. package/dist/src/cli/commands/check.js +25 -40
  16. package/dist/src/cli/commands/check.js.map +1 -1
  17. package/dist/src/cli/commands/contract.d.ts +3 -1
  18. package/dist/src/cli/commands/contract.js +8 -2
  19. package/dist/src/cli/commands/contract.js.map +1 -1
  20. package/dist/src/cli/commands/init/scaffold-ops.js +0 -2
  21. package/dist/src/cli/commands/init/scaffold-ops.js.map +1 -1
  22. package/dist/src/cli/commands/init.js +4 -3
  23. package/dist/src/cli/commands/init.js.map +1 -1
  24. package/dist/src/cli/commands/orchestrate.js +10 -3
  25. package/dist/src/cli/commands/orchestrate.js.map +1 -1
  26. package/dist/src/cli/commands/status.js.map +1 -1
  27. package/dist/src/cli/index.js +14 -19
  28. package/dist/src/cli/index.js.map +1 -1
  29. package/dist/src/cli/utils/compliance.d.ts +5 -1
  30. package/dist/src/cli/utils/compliance.js +90 -42
  31. package/dist/src/cli/utils/compliance.js.map +1 -1
  32. package/dist/src/cli/utils/ui.js +11 -11
  33. package/dist/src/cli/utils/ui.js.map +1 -1
  34. package/dist/src/modules/adapters/definitions.js +114 -113
  35. package/dist/src/modules/adapters/definitions.js.map +1 -1
  36. package/dist/src/modules/adapters/registry.d.ts +12 -0
  37. package/dist/src/modules/adapters/registry.js +19 -0
  38. package/dist/src/modules/adapters/registry.js.map +1 -0
  39. package/dist/src/modules/adapters/shared.js +5 -4
  40. package/dist/src/modules/adapters/shared.js.map +1 -1
  41. package/dist/src/modules/agents/definitions.d.ts +6 -6
  42. package/dist/src/modules/agents/definitions.js +49 -25
  43. package/dist/src/modules/agents/definitions.js.map +1 -1
  44. package/dist/src/modules/agents/registry/analyst.js +1 -1
  45. package/dist/src/modules/agents/registry/analyst.js.map +1 -1
  46. package/dist/src/modules/agents/registry/architect.js +1 -1
  47. package/dist/src/modules/agents/registry/architect.js.map +1 -1
  48. package/dist/src/modules/agents/registry/backend.js +1 -1
  49. package/dist/src/modules/agents/registry/backend.js.map +1 -1
  50. package/dist/src/modules/agents/registry/database.js +1 -1
  51. package/dist/src/modules/agents/registry/database.js.map +1 -1
  52. package/dist/src/modules/agents/registry/devops.js +1 -1
  53. package/dist/src/modules/agents/registry/devops.js.map +1 -1
  54. package/dist/src/modules/agents/registry/explorer.js +1 -1
  55. package/dist/src/modules/agents/registry/explorer.js.map +1 -1
  56. package/dist/src/modules/agents/registry/frontend.js +1 -1
  57. package/dist/src/modules/agents/registry/frontend.js.map +1 -1
  58. package/dist/src/modules/agents/registry/git.js +1 -1
  59. package/dist/src/modules/agents/registry/git.js.map +1 -1
  60. package/dist/src/modules/agents/registry/manager.js +1 -2
  61. package/dist/src/modules/agents/registry/manager.js.map +1 -1
  62. package/dist/src/modules/agents/registry/mobile.js +1 -1
  63. package/dist/src/modules/agents/registry/mobile.js.map +1 -1
  64. package/dist/src/modules/agents/registry/quality.js +1 -1
  65. package/dist/src/modules/agents/registry/quality.js.map +1 -1
  66. package/dist/src/modules/agents/registry/security.js +1 -1
  67. package/dist/src/modules/agents/registry/security.js.map +1 -1
  68. package/dist/src/shared/fs.js +4 -3
  69. package/dist/src/shared/fs.js.map +1 -1
  70. package/dist/tests/agents-definitions.test.js +2 -1
  71. package/dist/tests/agents-definitions.test.js.map +1 -1
  72. package/dist/tests/integration/hermes_locking.test.d.ts +1 -0
  73. package/dist/tests/integration/hermes_locking.test.js +62 -0
  74. package/dist/tests/integration/hermes_locking.test.js.map +1 -0
  75. package/dist/tests/orchestrate.test.js +9 -6
  76. package/dist/tests/orchestrate.test.js.map +1 -1
  77. package/framework-mcp/dist/index.js +11 -1
  78. package/framework-mcp/dist/tools/definitions.js +0 -10
  79. package/framework-mcp/dist/tools/index.js +0 -2
  80. package/framework-mcp/dist/utils/compliance.js +68 -19
  81. package/framework-mcp/node_modules/.package-lock.json +1179 -0
  82. package/framework-mcp/package-lock.json +1191 -0
  83. package/framework-mcp/package.json +2 -2
  84. package/framework-mcp/src/index.ts +13 -1
  85. package/framework-mcp/src/tools/definitions.ts +0 -10
  86. package/framework-mcp/src/tools/index.ts +0 -2
  87. package/framework-mcp/src/utils/compliance.ts +82 -24
  88. package/package.json +3 -4
  89. package/src/cli/adapters/scaffold.ts +9 -8
  90. package/src/cli/commands/check.ts +26 -48
  91. package/src/cli/commands/contract.ts +5 -2
  92. package/src/cli/commands/init/scaffold-ops.ts +0 -3
  93. package/src/cli/commands/init.ts +5 -3
  94. package/src/cli/commands/orchestrate.ts +12 -3
  95. package/src/cli/commands/status.ts +3 -2
  96. package/src/cli/index.ts +14 -20
  97. package/src/cli/utils/compliance.ts +101 -47
  98. package/src/cli/utils/ui.ts +11 -11
  99. package/src/modules/adapters/definitions.ts +82 -57
  100. package/src/modules/adapters/registry.ts +27 -0
  101. package/src/modules/adapters/shared.ts +5 -4
  102. package/src/modules/agents/definitions.ts +53 -24
  103. package/src/modules/agents/registry/analyst.ts +1 -1
  104. package/src/modules/agents/registry/architect.ts +1 -1
  105. package/src/modules/agents/registry/backend.ts +1 -1
  106. package/src/modules/agents/registry/database.ts +1 -1
  107. package/src/modules/agents/registry/devops.ts +1 -1
  108. package/src/modules/agents/registry/explorer.ts +1 -1
  109. package/src/modules/agents/registry/frontend.ts +1 -1
  110. package/src/modules/agents/registry/git.ts +1 -1
  111. package/src/modules/agents/registry/manager.ts +1 -2
  112. package/src/modules/agents/registry/mobile.ts +1 -1
  113. package/src/modules/agents/registry/quality.ts +1 -1
  114. package/src/modules/agents/registry/security.ts +1 -1
  115. package/src/shared/fs.ts +4 -3
  116. package/templates/standards/frontend-standards.md +7 -2
  117. package/templates/standards/governance-standards.md +27 -1
  118. package/templates/standards/logging-and-secrets.md +1 -1
  119. package/templates/standards/observability-standards.md +1 -1
  120. package/templates/standards/quality-standards.md +2 -1
  121. package/bin/enderun-dashboard.js +0 -25
  122. package/framework-mcp/src/tools/dashboard/start_dashboard.ts +0 -33
  123. package/src/cli/commands/dashboard/data-service.ts +0 -167
  124. package/src/cli/commands/dashboard/html-template.ts +0 -696
  125. package/src/cli/commands/dashboard/server.ts +0 -43
  126. package/src/cli/commands/dashboard.ts +0 -11
@@ -1,5 +1,6 @@
1
1
  import fs from "fs";
2
2
  import path from "path";
3
+ import ts from "typescript";
3
4
 
4
5
  interface ComplianceIssue {
5
6
  file: string;
@@ -7,75 +8,128 @@ interface ComplianceIssue {
7
8
  rule: string;
8
9
  }
9
10
 
10
- export function scanProjectCompliance(): ComplianceIssue[] {
11
+ /**
12
+ * Enterprise Compliance Scanner (AST-Based)
13
+ * Scans the project source code for governance and discipline violations.
14
+ */
15
+ export function scanProjectCompliance(targetDir: string = "src"): ComplianceIssue[] {
11
16
  const issues: ComplianceIssue[] = [];
12
- if (!fs.existsSync("src")) return [];
13
- const files = getAllFiles("src");
17
+ if (!fs.existsSync(targetDir)) return [];
18
+
19
+ const files = getAllFiles(targetDir);
14
20
 
15
21
  for (const file of files) {
16
- // Absolute skip for the scanner itself to avoid recursion/self-flagging
22
+ // Absolute skip for critical framework and utility files to avoid self-flagging
17
23
  if (file.includes("compliance") ||
18
24
  file.includes("definitions") ||
19
- file.includes("agents/registry") || // Skip agent definitions
20
- file.includes("scaffold-ops.ts") || // Template generator
25
+ file.includes("agents/registry") ||
26
+ file.includes("scaffold-ops.ts") ||
21
27
  file.includes("logger") ||
22
28
  file.includes("errors") ||
23
29
  file.includes("shared/fs")) continue;
24
30
 
25
31
  const content = fs.readFileSync(file, "utf8");
26
- const lines = content.split("\n");
32
+
33
+ // --- 1. AST-Based Analysis (For Language Rules) ---
34
+ const sourceFile = ts.createSourceFile(file, content, ts.ScriptTarget.Latest, true);
35
+
36
+ const visit = (node: ts.Node) => {
37
+ // Rule: No console.log allowed (excluding UI and warn/error helpers)
38
+ if (ts.isPropertyAccessExpression(node)) {
39
+ const expression = node.expression;
40
+ const name = node.name.text;
41
+ if (ts.isIdentifier(expression) && expression.text === "console" && name === "log") {
42
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
43
+ issues.push({ file, line: line + 1, rule: "No console.log allowed (Use logger instead)" });
44
+ }
45
+ }
27
46
 
28
- lines.forEach((line, index) => {
29
- const lineNum = index + 1;
30
- const trimmedLine = line.trim();
47
+ // Rule: No 'any' type usage
48
+ if (ts.isTypeReferenceNode(node)) {
49
+ if (ts.isIdentifier(node.typeName) && node.typeName.text === "any") {
50
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
51
+ issues.push({ file, line: line + 1, rule: "Usage of 'any' type is forbidden" });
52
+ }
53
+ }
54
+ if (node.kind === ts.SyntaxKind.AnyKeyword) {
55
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
56
+ issues.push({ file, line: line + 1, rule: "Usage of 'any' keyword is forbidden" });
57
+ }
31
58
 
32
- // Skip comments and common doc string patterns
33
- if (trimmedLine.startsWith("//") ||
34
- trimmedLine.startsWith("*") ||
35
- trimmedLine.startsWith("-") ||
36
- trimmedLine.includes("\": \"") || // Ignore JSON-like values
37
- trimmedLine.includes("default: ") || // Ignore default template values
38
- trimmedLine.startsWith("`")) return; // Ignore template literals
39
-
40
- // Only check if it's NOT inside a UI/console message
41
- const isUIMessage = line.includes("console.warn") || line.includes("console.error") || line.includes("UI.");
42
- const isTypeDefinition = line.includes("interface ") || line.includes("type ");
43
-
44
- // 1. Console.log check - only if it's actual code
45
- if (line.includes("console.log") && !isUIMessage) {
46
- issues.push({ file, line: lineNum, rule: "No console.log allowed" });
59
+ // Rule: Zero UI Library Policy
60
+ if (ts.isImportDeclaration(node)) {
61
+ const moduleSpecifier = node.moduleSpecifier;
62
+ if (ts.isStringLiteral(moduleSpecifier)) {
63
+ const forbiddenLibs = ["@chakra-ui", "mui", "@shadcn", "antd", "bootstrap"];
64
+ const lib = forbiddenLibs.find(l => moduleSpecifier.text.includes(l));
65
+ if (lib) {
66
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
67
+ issues.push({ file, line: line + 1, rule: `Forbidden UI library '${lib}' usage detected` });
68
+ }
69
+ }
47
70
  }
48
71
 
49
- // 2. Secret check (Regex for simple API keys/assignments) - skip UI messages and templates
50
- if (/(API_KEY|SECRET|PASSWORD)\s*[:=]/i.test(line) &&
51
- !file.includes(".env") &&
52
- !isUIMessage &&
53
- !trimmedLine.includes(".env.example") && // Skip template lines
54
- !trimmedLine.includes("your_secret_key_here") && // Skip common placeholders
55
- !trimmedLine.includes("pattern:")) {
56
- issues.push({ file, line: lineNum, rule: "Potential hardcoded secret assignment detected" });
72
+ // Rule: Raw fs mutation check (Use atomic utilities)
73
+ if (ts.isCallExpression(node) && ts.isPropertyAccessExpression(node.expression)) {
74
+ const prop = node.expression;
75
+ if (ts.isIdentifier(prop.expression) && prop.expression.text === "fs") {
76
+ if (["writeFileSync", "appendFileSync"].includes(prop.name.text)) {
77
+ // Skip if it's explicitly allowed (e.g. lock files)
78
+ const args = node.arguments;
79
+ let isLockFile = false;
80
+
81
+ if (args.length > 0) {
82
+ const firstArgText = args[0].getText(sourceFile).toLowerCase();
83
+ if (firstArgText.includes("lock")) {
84
+ isLockFile = true;
85
+ }
86
+ }
87
+
88
+ if (!isLockFile) {
89
+ const { line } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
90
+ issues.push({ file, line: line + 1, rule: "Use atomic utilities (writeTextFile) instead of raw fs mutations" });
91
+ }
92
+ }
93
+ }
57
94
  }
58
95
 
59
- // 3. 'Any' type check
60
- if (/\bany\b/.test(line) && !isTypeDefinition && !trimmedLine.includes("\":")) {
61
- issues.push({ file, line: lineNum, rule: "Usage of 'any' type is forbidden" });
96
+ ts.forEachChild(node, visit);
97
+ };
98
+
99
+ visit(sourceFile);
100
+
101
+ // --- 2. Pattern-Based Analysis (For Secrets & PII & Debt) ---
102
+ const lines = content.split("\n");
103
+ const piiKeywords = [
104
+ { regex: /API_KEY\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded API Key" },
105
+ { regex: /SECRET\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded Secret" },
106
+ { regex: /PASSWORD\s*[:=]\s*['"][^'"]+['"]/i, msg: "Hardcoded Password" },
107
+ { regex: /[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}/, msg: "PII Detected: Email Address" },
108
+ { regex: /\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b/, msg: "PII Detected: Credit Card Pattern" }
109
+ ];
110
+
111
+ lines.forEach((line, index) => {
112
+ const lineNum = index + 1;
113
+ const trimmedLine = line.trim();
114
+
115
+ if (trimmedLine.startsWith("//") || trimmedLine.startsWith("*")) return;
116
+
117
+ // PII & Secret Guard
118
+ for (const { regex, msg } of piiKeywords) {
119
+ if (regex.test(line)) {
120
+ // Allow emails in specific files
121
+ if (msg.includes("Email") && (file.endsWith("README.md") || file.endsWith("package.json") || file.includes("CONTRIBUTING"))) {
122
+ continue;
123
+ }
124
+ issues.push({ file, line: lineNum, rule: `Corporate Compliance Breach: ${msg}` });
125
+ }
62
126
  }
63
127
 
64
- // 4. Technical Debt Detection (TODO/FIXME)
128
+ // Technical Debt (TODO/FIXME)
65
129
  if (/\b(TODO|FIXME)\b/i.test(line)) {
66
130
  issues.push({ file, line: lineNum, rule: "Unresolved Technical Debt (TODO/FIXME) found" });
67
131
  }
68
-
69
- // 5. Raw fs write check (Atomic safety enforcement)
70
- if ((line.includes("fs.writeFileSync") || line.includes("fs.appendFileSync")) &&
71
- !file.includes("src/shared/fs.ts") && // Ignore the utility itself
72
- !line.includes(".lock") && // Allow lock files
73
- !line.includes("lockPath") && // Allow lock path variables
74
- !file.includes("bin/")) { // Ignore standalone binary scripts
75
- issues.push({ file, line: lineNum, rule: "Use internal atomic utilities (writeTextFile) instead of raw fs" });
76
- }
77
132
  });
78
-
79
133
  }
80
134
  return issues;
81
135
  }
@@ -10,8 +10,8 @@ export const UI = {
10
10
  * Renders a strategic intent block similar to Gemini CLI's topic updates.
11
11
  */
12
12
  intent: (title: string, intent: string) => {
13
- console.warn(`\n${chalk.bold.cyan(title)}:`);
14
- console.warn(`${chalk.italic.gray(` ${intent}`)}\n`);
13
+ process.stdout.write(`\n${chalk.bold.cyan(title)}:\n`);
14
+ process.stdout.write(`${chalk.italic.gray(` ${intent}`)}\n\n`);
15
15
  },
16
16
 
17
17
  /**
@@ -21,39 +21,39 @@ export const UI = {
21
21
  const width = process.stdout.columns || 80;
22
22
  const line = "─".repeat(Math.max(0, width - 2));
23
23
 
24
- console.warn(chalk.gray(`╭${line}╮`));
24
+ process.stdout.write(chalk.gray(`╭${line}╮`) + "\n");
25
25
  const padding = Math.max(0, width - agentName.length - 27);
26
- console.warn(`${chalk.gray("│")} ${chalk.bold.yellow("≡")} ${chalk.white(`Running Agent: ${chalk.bold.green(agentName)}...`)} ${chalk.gray("(ctrl+o to expand)".padStart(padding))} ${chalk.gray("│")}`);
26
+ process.stdout.write(`${chalk.gray("│")} ${chalk.bold.yellow("≡")} ${chalk.white(`Running Agent: ${chalk.bold.green(agentName)}...`)} ${chalk.gray("(ctrl+o to expand)".padStart(padding))} ${chalk.gray("│")}\n`);
27
27
  if (action) {
28
28
  const paddingAction = Math.max(0, width - action.length - 6);
29
- console.warn(`${chalk.gray("│")} ${chalk.blue("!")} ${chalk.cyan(action)} ${" ".repeat(paddingAction)} ${chalk.gray("│")}`);
29
+ process.stdout.write(`${chalk.gray("│")} ${chalk.blue("!")} ${chalk.cyan(action)} ${" ".repeat(paddingAction)} ${chalk.gray("│")}\n`);
30
30
  }
31
31
  if (details) {
32
32
  const paddingDetails = Math.max(0, width - details.length - 7);
33
- console.warn(`${chalk.gray("│")} ${chalk.gray(details.slice(0, width - 10))} ${" ".repeat(paddingDetails)} ${chalk.gray("│")}`);
33
+ process.stdout.write(`${chalk.gray("│")} ${chalk.gray(details.slice(0, width - 10))} ${" ".repeat(paddingDetails)} ${chalk.gray("│")}\n`);
34
34
  }
35
- console.warn(chalk.gray(`╰${line}╯`));
35
+ process.stdout.write(chalk.gray(`╰${line}╯`) + "\n");
36
36
  },
37
37
 
38
38
  /**
39
39
  * Renders a success status message.
40
40
  */
41
41
  success: (msg: string) => {
42
- console.warn(`${chalk.bold.green("✅")} ${msg}`);
42
+ process.stdout.write(`${chalk.bold.green("✅")} ${msg}\n`);
43
43
  },
44
44
 
45
45
  /**
46
46
  * Renders an error status message.
47
47
  */
48
48
  error: (msg: string) => {
49
- console.error(`${chalk.bold.red("❌")} ${chalk.red(msg)}`);
49
+ process.stderr.write(`${chalk.bold.red("❌")} ${chalk.red(msg)}\n`);
50
50
  },
51
51
 
52
52
  /**
53
53
  * Renders a warning status message.
54
54
  */
55
55
  warning: (msg: string) => {
56
- console.warn(`${chalk.bold.yellow("⚠️")} ${chalk.yellow(msg)}`);
56
+ process.stdout.write(`${chalk.bold.yellow("⚠️")} ${chalk.yellow(msg)}\n`);
57
57
  },
58
58
 
59
59
  /**
@@ -61,6 +61,6 @@ export const UI = {
61
61
  */
62
62
  divider: () => {
63
63
  const width = process.stdout.columns || 80;
64
- console.warn(chalk.gray("─".repeat(width)));
64
+ process.stdout.write(chalk.gray("─".repeat(width)) + "\n");
65
65
  }
66
66
  };
@@ -3,13 +3,14 @@ import type { AdapterConfig, AdapterId } from "./types.js";
3
3
  import { addMcpServerToClaude, findClaudeConfigPath } from "../../cli/utils/claude.js";
4
4
  import { writeJsonFile } from "../../shared/fs.js";
5
5
  import { registerGlobalAntigravityPlugins } from "./shared.js";
6
- import { unifiedAdapterPath, UNIFIED_ADAPTER_SLUG } from "../../shared/constants.js";
6
+ import { registry } from "./registry.js";
7
+ import { UI } from "../../cli/utils/ui.js";
7
8
 
8
- /**
9
- * Mapped Adapter Configurations
10
- */
11
- export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
12
- gemini: {
9
+ // ─── Register Core Adapters ──────────────────────────────────────────────────
10
+
11
+ // ♊ Gemini
12
+ registry.register(
13
+ {
13
14
  id: "gemini",
14
15
  frameworkDir: ".gemini",
15
16
  shimFile: "GEMINI.md",
@@ -17,10 +18,20 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
17
18
  role: "commander",
18
19
  templateDir: ".enderun",
19
20
  nestedDirs: ["agents", "rules"],
20
- agentsDir: ".gemini/agents", // Use unified path
21
+ agentsDir: ".gemini/agents",
21
22
  agentsExt: ".md"
22
23
  },
23
- claude: {
24
+ (projectRoot, mcpBlock) => {
25
+ const frameworkDir = ".gemini";
26
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp.json"), mcpBlock);
27
+ UI.success(`Gemini MCP registered → ${frameworkDir}/mcp.json`);
28
+ registerGlobalAntigravityPlugins(mcpBlock);
29
+ }
30
+ );
31
+
32
+ // 🚀 Claude
33
+ registry.register(
34
+ {
24
35
  id: "claude",
25
36
  frameworkDir: ".claude",
26
37
  shimFile: "CLAUDE.md",
@@ -31,7 +42,24 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
31
42
  agentsDir: ".claude/agents",
32
43
  agentsExt: ".md"
33
44
  },
34
- grok: {
45
+ (projectRoot, mcpBlock) => {
46
+ const configPath = findClaudeConfigPath();
47
+ if (configPath) {
48
+ const block = mcpBlock as { mcpServers: Record<string, unknown> };
49
+ const mcpEntry = block.mcpServers["agent-enderun"] as Record<string, unknown>;
50
+ const ok = addMcpServerToClaude(configPath, "agent-enderun", mcpEntry);
51
+ if (ok) UI.success(`Claude MCP registered → ${configPath}`);
52
+ }
53
+ const frameworkDir = ".claude";
54
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
55
+ writeJsonFile(path.join(projectRoot, ".mcp.json"), mcpBlock);
56
+ UI.success("Claude Code Project MCP → .mcp.json");
57
+ }
58
+ );
59
+
60
+ // 🤖 Grok
61
+ registry.register(
62
+ {
35
63
  id: "grok",
36
64
  frameworkDir: ".grok",
37
65
  shimFile: "GROK.md",
@@ -42,7 +70,16 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
42
70
  agentsDir: ".grok/agents",
43
71
  agentsExt: ".md"
44
72
  },
45
- cursor: {
73
+ (projectRoot, mcpBlock) => {
74
+ const frameworkDir = ".grok";
75
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
76
+ UI.success(`Grok MCP → ${frameworkDir}/mcp_config.json`);
77
+ }
78
+ );
79
+
80
+ // ✍️ Cursor
81
+ registry.register(
82
+ {
46
83
  id: "cursor",
47
84
  frameworkDir: ".cursor",
48
85
  shimFile: "CURSOR.md",
@@ -53,7 +90,16 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
53
90
  agentsDir: ".cursor/rules",
54
91
  agentsExt: ".mdc"
55
92
  },
56
- codex: {
93
+ (projectRoot, mcpBlock) => {
94
+ const frameworkDir = ".cursor";
95
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp.json"), mcpBlock);
96
+ UI.success(`Cursor IDE Project MCP → ${frameworkDir}/mcp.json`);
97
+ }
98
+ );
99
+
100
+ // 💡 Codex (Copilot)
101
+ registry.register(
102
+ {
57
103
  id: "codex",
58
104
  frameworkDir: ".agents",
59
105
  shimFile: "copilot-instructions.md",
@@ -64,7 +110,18 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
64
110
  agentsDir: ".agents/instructions",
65
111
  agentsExt: ".md"
66
112
  },
67
- "antigravity-cli": {
113
+ (projectRoot, mcpBlock) => {
114
+ const frameworkDir = ".agents";
115
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
116
+ writeJsonFile(path.join(projectRoot, ".vscode/mcp.json"), mcpBlock);
117
+ writeJsonFile(path.join(projectRoot, ".mcp.json"), mcpBlock);
118
+ UI.success("GitHub Copilot Project MCP → .vscode/mcp.json & .mcp.json");
119
+ }
120
+ );
121
+
122
+ // 🛸 Antigravity
123
+ registry.register(
124
+ {
68
125
  id: "antigravity-cli",
69
126
  frameworkDir: ".antigravity",
70
127
  shimFile: "AGENTS.md",
@@ -74,53 +131,21 @@ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = {
74
131
  nestedDirs: ["agents", "plugins", "rules"],
75
132
  agentsDir: ".antigravity/agents",
76
133
  agentsExt: ".md"
134
+ },
135
+ (projectRoot, mcpBlock) => {
136
+ const frameworkDir = ".antigravity";
137
+ writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
138
+ UI.success(`Antigravity CLI MCP → ${frameworkDir}/mcp_config.json`);
139
+ registerGlobalAntigravityPlugins(mcpBlock);
77
140
  }
78
- };
141
+ );
142
+
143
+ /**
144
+ * Mapped Adapter Configurations
145
+ */
146
+ export const ADAPTER_CONFIGS: Record<AdapterId, AdapterConfig> = registry.getConfigs();
79
147
 
80
148
  /**
81
149
  * Post-Initialization Handlers for specific adapters
82
150
  */
83
- export const POST_INIT_HANDLERS: Record<AdapterId, (projectRoot: string, mcpBlock: unknown) => void> = {
84
- gemini: (projectRoot, mcpBlock) => {
85
- const frameworkDir = ADAPTER_CONFIGS.gemini.frameworkDir;
86
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp.json"), mcpBlock);
87
- console.warn(`✅ Gemini MCP registered → ${frameworkDir}/mcp.json`);
88
- registerGlobalAntigravityPlugins(mcpBlock);
89
- },
90
- claude: (projectRoot, mcpBlock) => {
91
- const configPath = findClaudeConfigPath();
92
- if (configPath) {
93
- const block = mcpBlock as { mcpServers: Record<string, unknown> };
94
- const mcpEntry = block.mcpServers["agent-enderun"] as Record<string, unknown>;
95
- const ok = addMcpServerToClaude(configPath, "agent-enderun", mcpEntry);
96
- if (ok) console.warn(`✅ Claude MCP registered → ${configPath}`);
97
- }
98
- const frameworkDir = ADAPTER_CONFIGS.claude.frameworkDir;
99
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
100
- writeJsonFile(path.join(projectRoot, ".mcp.json"), mcpBlock);
101
- console.warn("✅ Claude Code Project MCP → .mcp.json");
102
- },
103
- grok: (projectRoot, mcpBlock) => {
104
- const frameworkDir = ADAPTER_CONFIGS.grok.frameworkDir;
105
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
106
- console.warn(`✅ Grok MCP → ${frameworkDir}/mcp_config.json`);
107
- },
108
- cursor: (projectRoot, mcpBlock) => {
109
- const frameworkDir = ADAPTER_CONFIGS.cursor.frameworkDir;
110
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp.json"), mcpBlock);
111
- console.warn(`✅ Cursor IDE Project MCP → ${frameworkDir}/mcp.json`);
112
- },
113
- codex: (projectRoot, mcpBlock) => {
114
- const frameworkDir = ADAPTER_CONFIGS.codex.frameworkDir;
115
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
116
- writeJsonFile(path.join(projectRoot, ".vscode/mcp.json"), mcpBlock);
117
- writeJsonFile(path.join(projectRoot, ".mcp.json"), mcpBlock);
118
- console.warn("✅ GitHub Copilot Project MCP → .vscode/mcp.json & .mcp.json");
119
- },
120
- "antigravity-cli": (projectRoot, mcpBlock) => {
121
- const frameworkDir = ADAPTER_CONFIGS["antigravity-cli"].frameworkDir;
122
- writeJsonFile(path.join(projectRoot, frameworkDir, "mcp_config.json"), mcpBlock);
123
- console.warn(`✅ Antigravity CLI MCP → ${frameworkDir}/mcp_config.json`);
124
- registerGlobalAntigravityPlugins(mcpBlock);
125
- }
126
- };
151
+ export const POST_INIT_HANDLERS: Record<AdapterId, (projectRoot: string, mcpBlock: unknown) => void> = registry.getHandlers();
@@ -0,0 +1,27 @@
1
+ import { AdapterId, AdapterConfig } from "./types.js";
2
+
3
+ type PostInitHandler = (projectRoot: string, mcpBlock: unknown) => void;
4
+
5
+ class AdapterRegistry {
6
+ private configs: Map<string, AdapterConfig> = new Map();
7
+ private handlers: Map<string, PostInitHandler> = new Map();
8
+
9
+ register(config: AdapterConfig, handler: PostInitHandler) {
10
+ this.configs.set(config.id, config);
11
+ this.handlers.set(config.id, handler);
12
+ }
13
+
14
+ getConfigs(): Record<string, AdapterConfig> {
15
+ return Object.fromEntries(this.configs);
16
+ }
17
+
18
+ getHandlers(): Record<string, PostInitHandler> {
19
+ return Object.fromEntries(this.handlers);
20
+ }
21
+
22
+ getIds(): AdapterId[] {
23
+ return Array.from(this.configs.keys()) as AdapterId[];
24
+ }
25
+ }
26
+
27
+ export const registry = new AdapterRegistry();
@@ -6,6 +6,7 @@ import { writeJsonFile, writeTextFile } from "../../shared/fs.js";
6
6
  import { ALL_AGENTS, toAntigravityJson } from "../agents/definitions.js";
7
7
  import { CORE_SKILLS } from "../skills/definitions.js";
8
8
  import { getPackageRoot } from "../../cli/utils/pkg.js";
9
+ import { logger } from "../../shared/logger.js";
9
10
 
10
11
  export function registerGlobalAntigravityPlugins(mcpBlock: unknown): void {
11
12
  // Allow overriding via env var for different OS/Gemini CLI versions
@@ -20,7 +21,7 @@ export function registerGlobalAntigravityPlugins(mcpBlock: unknown): void {
20
21
  fs.mkdirSync(globalDir, { recursive: true });
21
22
  writeJsonFile(path.join(globalDir, "mcp.json"), mcpBlock);
22
23
  writeJsonFile(path.join(globalDir, "mcp_config.json"), mcpBlock);
23
- console.warn(`✅ Antigravity MCP registered → ${globalDir}/`);
24
+ logger.info(`Antigravity MCP registered → ${globalDir}/`);
24
25
 
25
26
  // Global Plugin configuration under plugins/agent-enderun/
26
27
  const globalPluginDir = path.join(globalDir, "plugins/agent-enderun");
@@ -103,17 +104,17 @@ ${ag.instructions.rules.map(r => `- ${r}`).join("\n")}
103
104
  // Scaffold optional empty hooks.json
104
105
  writeJsonFile(path.join(globalPluginDir, "hooks.json"), {});
105
106
 
106
- console.warn(`✅ Antigravity Plugin registered → ${globalPluginDir}/`);
107
+ logger.info(`Antigravity Plugin registered → ${globalPluginDir}/`);
107
108
 
108
109
  try {
109
110
  execSync(`agy plugin install "${globalPluginDir}"`, { stdio: "ignore" });
110
- console.warn(`✅ Antigravity Plugin installed in CLI.`);
111
+ logger.info("Antigravity Plugin installed in CLI.");
111
112
  } catch {
112
113
  // Ignore if agy is not in PATH or fails
113
114
  }
114
115
 
115
116
  } catch (e) {
116
- console.warn(`⚠️ Failed to register plugin/MCP in ${globalDir}:`, e);
117
+ logger.warn(`Failed to register plugin/MCP in ${globalDir}`, e);
117
118
  }
118
119
  }
119
120
  }