agent-enderun 0.8.8 → 0.9.1

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 (164) hide show
  1. package/.enderun/BRAIN_DASHBOARD.md +5 -5
  2. package/{ENDERUN.md → .enderun/ENDERUN.md} +4 -2
  3. package/.enderun/PROJECT_MEMORY.md +32 -43
  4. package/.enderun/STATUS.md +25 -28
  5. package/.enderun/agents/agent_army_schema.json +29 -7
  6. package/.enderun/agents/backend.md +48 -39
  7. package/.enderun/agents/database.md +55 -0
  8. package/.enderun/agents/devops.md +30 -17
  9. package/.enderun/agents/explorer.md +5 -5
  10. package/.enderun/agents/frontend.md +97 -76
  11. package/.enderun/agents/git.md +4 -4
  12. package/.enderun/agents/manager.md +68 -59
  13. package/.enderun/agents/mobile.md +4 -4
  14. package/.enderun/agents/native.md +106 -0
  15. package/.enderun/agents/quality.md +122 -0
  16. package/.enderun/agents/schema/agent-lifecycle-schema.json +29 -8
  17. package/.enderun/cli-commands.json +7 -3
  18. package/.enderun/config.json +8 -2
  19. package/.enderun/knowledge/SHIM_TEMPLATE.md +25 -0
  20. package/.enderun/knowledge/context_boundary_rules.md +57 -0
  21. package/.enderun/knowledge/corporate-governance/high-risk-action-approval-flow.md +29 -0
  22. package/.enderun/knowledge/documentation_ownership.md +53 -0
  23. package/.enderun/knowledge/eslint-standards.md +63 -0
  24. package/.enderun/knowledge/frontend_professionalization_guidelines.md +7 -7
  25. package/.enderun/knowledge/hermes_live_test_guidelines.md +5 -5
  26. package/.enderun/knowledge/hermes_protocol.md +1 -1
  27. package/.enderun/knowledge/lessons-learned.md +14 -0
  28. package/.enderun/knowledge/manager_authority_audit_enforcement.md +10 -10
  29. package/.enderun/knowledge/project_scaffold_guidelines.md +4 -4
  30. package/.enderun/knowledge/reference_application_guidelines.md +6 -6
  31. package/.enderun/mcp_config.json +9 -0
  32. package/.enderun/memory-graph/agent-contexts/backend.json +1 -1
  33. package/.enderun/memory-graph/agent-contexts/devops.json +1 -1
  34. package/.enderun/memory-graph/agent-contexts/explorer.json +1 -1
  35. package/.enderun/memory-graph/agent-contexts/frontend.json +1 -1
  36. package/.enderun/memory-graph/agent-contexts/git.json +1 -1
  37. package/.enderun/memory-graph/agent-contexts/manager.json +1 -1
  38. package/.enderun/memory-graph/agent-contexts/mobile.json +1 -1
  39. package/.enderun/memory-graph/agent-contexts/native.json +1 -0
  40. package/.enderun/memory-graph/agent-contexts/quality.json +1 -0
  41. package/.enderun/memory-graph/graph.json +123 -15
  42. package/.enderun/memory-graph/shared-facts.json +26 -31
  43. package/.enderun/queue/README.md +13 -13
  44. package/README.md +171 -393
  45. package/agent.md +25 -0
  46. package/bin/cli.js +10 -10
  47. package/bin/init-check.js +69 -0
  48. package/bin/update-contract.js +57 -38
  49. package/bin/validate-agent-army.js +38 -13
  50. package/claude.md +14 -8
  51. package/docs/README.md +24 -16
  52. package/docs/architecture/README.md +9 -0
  53. package/docs/architecture/standards/data-fetching-patterns.md +13 -0
  54. package/docs/architecture/standards/design-system.md +31 -0
  55. package/docs/architecture/standards/logging.md +7 -0
  56. package/docs/architecture/standards/tech-stack.md +9 -0
  57. package/docs/getting-started.md +9 -259
  58. package/docs/user/README.md +35 -0
  59. package/docs/user/action-plan-2026.md +9 -0
  60. package/docs/user/getting-started.md +13 -0
  61. package/docs/user/roadmap.md +13 -0
  62. package/eslint.config.js +68 -0
  63. package/framework-mcp/dist/index.js +178 -24
  64. package/framework-mcp/package.json +6 -33
  65. package/framework-mcp/tsconfig.json +3 -6
  66. package/gemini.md +12 -6
  67. package/grok.md +25 -0
  68. package/package.json +13 -5
  69. package/panda.config.ts +86 -86
  70. package/src/cli/adapters.ts +208 -0
  71. package/src/cli/commands/app.ts +38 -0
  72. package/src/cli/commands/check.ts +87 -0
  73. package/src/cli/commands/compliance.ts +55 -0
  74. package/src/cli/commands/contract.ts +45 -0
  75. package/src/cli/commands/explorer.ts +45 -0
  76. package/src/cli/commands/git.ts +39 -0
  77. package/src/cli/commands/init.ts +272 -0
  78. package/src/cli/commands/knowledge.ts +44 -0
  79. package/src/cli/commands/lint.ts +25 -0
  80. package/src/cli/commands/log.ts +37 -0
  81. package/src/cli/commands/memory.ts +78 -0
  82. package/src/cli/commands/orchestrate.ts +111 -0
  83. package/src/cli/commands/script.ts +20 -0
  84. package/src/cli/commands/security.ts +38 -0
  85. package/src/cli/commands/status.ts +59 -0
  86. package/src/cli/commands/trace.ts +46 -0
  87. package/src/cli/index.ts +74 -2155
  88. package/src/cli/utils/app.ts +764 -0
  89. package/src/cli/utils/claude.ts +56 -0
  90. package/src/cli/utils/fs.ts +139 -0
  91. package/src/cli/utils/memory.ts +141 -0
  92. package/src/cli/utils/pkg.ts +215 -0
  93. package/src/cli/utils/string.ts +48 -0
  94. package/src/cli/utils/time.ts +27 -0
  95. package/tsconfig.json +9 -0
  96. package/.enderun/agents/analyst.md +0 -238
  97. package/.enderun/agents/orchestrator.md +0 -172
  98. package/.enderun/agents/qa.md +0 -124
  99. package/.enderun/agents/security.md +0 -202
  100. package/.enderun/knowledge/api_design_rules.md +0 -6
  101. package/.enderun/knowledge/branded_types_pattern.md +0 -8
  102. package/.enderun/knowledge/code_review_checklist.md +0 -7
  103. package/.enderun/knowledge/contract_versioning.md +0 -7
  104. package/.enderun/knowledge/database_migration.md +0 -6
  105. package/.enderun/knowledge/deployment_checklist.md +0 -7
  106. package/.enderun/knowledge/git_commit_strategy.md +0 -10
  107. package/.enderun/knowledge/monitoring_setup.md +0 -5
  108. package/.enderun/knowledge/performance_guidelines.md +0 -11
  109. package/.enderun/knowledge/repository_patterns.md +0 -9
  110. package/.enderun/knowledge/security_scanning.md +0 -6
  111. package/.enderun/knowledge/testing_standards.md +0 -7
  112. package/.enderun/knowledge/troubleshooting_guide.md +0 -5
  113. package/.enderun/logs/analyst.json +0 -1
  114. package/.enderun/logs/backend.json +0 -1
  115. package/.enderun/logs/devops.json +0 -1
  116. package/.enderun/logs/explorer.json +0 -1
  117. package/.enderun/logs/frontend.json +0 -1
  118. package/.enderun/logs/git.json +0 -1
  119. package/.enderun/logs/manager.json +0 -1
  120. package/.enderun/logs/mobile.json +0 -1
  121. package/.enderun/logs/native.json +0 -1
  122. package/.enderun/logs/orchestrator.json +0 -1
  123. package/.enderun/logs/qa.json +0 -1
  124. package/.enderun/logs/security.json +0 -1
  125. package/.enderun/memory-graph/agent-contexts/analyst.json +0 -1
  126. package/.enderun/memory-graph/agent-contexts/orchestrator.json +0 -1
  127. package/.enderun/memory-graph/agent-contexts/qa.json +0 -1
  128. package/.enderun/memory-graph/agent-contexts/security.json +0 -1
  129. package/.env.example +0 -10
  130. package/cursor.md +0 -19
  131. package/docs/action-plan-2026.md +0 -119
  132. package/docs/roadmap.md +0 -142
  133. package/framework-mcp/README.md +0 -55
  134. package/framework-mcp/dist/schemas.js +0 -84
  135. package/framework-mcp/dist/tools/codebase.js +0 -294
  136. package/framework-mcp/dist/tools/contract.js +0 -197
  137. package/framework-mcp/dist/tools/framework.js +0 -225
  138. package/framework-mcp/dist/tools/git.js +0 -51
  139. package/framework-mcp/dist/tools/governance.js +0 -696
  140. package/framework-mcp/dist/tools/index.js +0 -47
  141. package/framework-mcp/dist/tools/knowledge.js +0 -133
  142. package/framework-mcp/dist/tools/memory.js +0 -217
  143. package/framework-mcp/dist/tools/messages.js +0 -193
  144. package/framework-mcp/dist/tools/monitoring.js +0 -294
  145. package/framework-mcp/dist/tools/orchestration.js +0 -369
  146. package/framework-mcp/dist/tools/pipeline.js +0 -267
  147. package/framework-mcp/dist/tools/security.js +0 -137
  148. package/framework-mcp/dist/utils.js +0 -121
  149. package/framework-mcp/src/index.ts +0 -49
  150. package/framework-mcp/src/schemas.ts +0 -106
  151. package/framework-mcp/src/tools/codebase.ts +0 -284
  152. package/framework-mcp/src/tools/contract.ts +0 -221
  153. package/framework-mcp/src/tools/framework.ts +0 -223
  154. package/framework-mcp/src/tools/git.ts +0 -42
  155. package/framework-mcp/src/tools/governance.ts +0 -891
  156. package/framework-mcp/src/tools/index.ts +0 -50
  157. package/framework-mcp/src/tools/knowledge.ts +0 -141
  158. package/framework-mcp/src/tools/memory.ts +0 -207
  159. package/framework-mcp/src/tools/messages.ts +0 -213
  160. package/framework-mcp/src/tools/monitoring.ts +0 -351
  161. package/framework-mcp/src/tools/orchestration.ts +0 -440
  162. package/framework-mcp/src/tools/pipeline.ts +0 -353
  163. package/framework-mcp/src/tools/security.ts +0 -143
  164. package/framework-mcp/src/utils.ts +0 -130
@@ -1,696 +0,0 @@
1
- /**
2
- * Governance Tools - MCP Tools for enforcing corporate governance standards
3
- *
4
- * These tools bridge the .enderun/knowledge/corporate-governance/ knowledge base
5
- * with actual code-level compliance verification.
6
- *
7
- * Philosophy: We do not just document standards — we actively verify them.
8
- *
9
- * All tools now return a standardized GovernanceToolResult format for consistency.
10
- */
11
- import fs from "fs";
12
- import path from "path";
13
- import { Project, SyntaxKind } from "ts-morph";
14
- // --- Shared Helper Functions ---
15
- function createEmptyGovernanceReport() {
16
- return {
17
- compliance_score: 0,
18
- total_checks: 0,
19
- violations: [],
20
- passed_checks: 0,
21
- summary: "",
22
- recommendations: [],
23
- };
24
- }
25
- /**
26
- * Creates a standardized result object for Governance tools.
27
- */
28
- function createGovernanceToolResult(toolName,
29
- // Dynamic tool output shape
30
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
31
- data, summary, success = true, error) {
32
- const base = {
33
- tool: toolName,
34
- success,
35
- executed_at: new Date().toISOString(),
36
- summary,
37
- data,
38
- error,
39
- };
40
- // Eğer data bir GovernanceReport ise, ekstra alanları doldur
41
- if (data && typeof data === "object" && "compliance_score" in data) {
42
- const report = data;
43
- base.score = report.compliance_score;
44
- base.violations_count = report.violations.length;
45
- }
46
- return base;
47
- }
48
- // --- Helper Functions ---
49
- function collectTypeFiles(dir) {
50
- const files = [];
51
- if (!fs.existsSync(dir))
52
- return files;
53
- const entries = fs.readdirSync(dir, { withFileTypes: true });
54
- for (const entry of entries) {
55
- const fullPath = path.join(dir, entry.name);
56
- if (entry.isDirectory()) {
57
- files.push(...collectTypeFiles(fullPath));
58
- }
59
- else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
60
- files.push(fullPath);
61
- }
62
- }
63
- return files;
64
- }
65
- function getFunctionLikeName(node) {
66
- if (!node)
67
- return '<anonymous>';
68
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
69
- if (typeof node.getName === 'function') {
70
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
- const n = node.getName();
72
- if (n)
73
- return n;
74
- }
75
- const parent = node.getParent?.();
76
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
77
- if (parent && typeof parent.getName === 'function') {
78
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
79
- const pn = parent.getName();
80
- if (pn)
81
- return pn;
82
- }
83
- return '<anonymous>';
84
- }
85
- function getFunctionLikeParameters(node) {
86
- if (!node)
87
- return [];
88
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
89
- if (typeof node.getParameters === 'function') {
90
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
91
- return node.getParameters();
92
- }
93
- return [];
94
- }
95
- function collectSourceFiles(dir) {
96
- const files = [];
97
- if (!fs.existsSync(dir))
98
- return files;
99
- const entries = fs.readdirSync(dir, { withFileTypes: true });
100
- for (const entry of entries) {
101
- const fullPath = path.join(dir, entry.name);
102
- if (entry.isDirectory()) {
103
- files.push(...collectSourceFiles(fullPath));
104
- }
105
- else if (entry.name.endsWith(".ts") || entry.name.endsWith(".tsx")) {
106
- files.push(fullPath);
107
- }
108
- }
109
- return files;
110
- }
111
- /**
112
- * Creates a standard ts-morph Project instance for analysis.
113
- */
114
- function createAnalysisProject(projectRoot) {
115
- return new Project({
116
- tsConfigFilePath: path.join(projectRoot, "tsconfig.json"),
117
- skipAddingFilesFromTsConfig: true,
118
- });
119
- }
120
- /**
121
- * Returns high-risk action patterns.
122
- *
123
- * Currently uses a stable, curated list derived from corporate-governance standards.
124
- * This approach is much more reliable than parsing markdown files with regex.
125
- *
126
- * In the future, this can be expanded to read from a structured JSON/YAML file
127
- * if more dynamic rule management is needed.
128
- */
129
- function loadHighRiskPatterns(_projectRoot) {
130
- // Stable, well-defined high-risk keywords based on corporate governance standards
131
- // (high-risk-actions-managerapproval.md and related documents)
132
- return [
133
- // Role & Permission elevation
134
- "promote", "elevate", "superadmin", "admin", "role", "permission", "grant", "revoke",
135
- // Destructive operations
136
- "delete", "purge", "bulk",
137
- // System configuration & reset
138
- "reset", "configure", "config", "system",
139
- // Sensitive data operations
140
- "export", "audit"
141
- ];
142
- }
143
- /**
144
- * Determines if a function is likely performing a high-risk administrative action.
145
- * This is the central detection logic for verify_high_risk_compliance.
146
- */
147
- function isHighRiskFunction(functionName, bodyText, highRiskKeywords) {
148
- const name = functionName.toLowerCase();
149
- const body = bodyText.toLowerCase();
150
- const combined = name + body;
151
- // 1. Temel yüksek riskli kelimeler
152
- const hasKeyword = highRiskKeywords.some(kw => name.includes(kw) || body.includes(kw));
153
- // 2. Route ve endpoint pattern'ları (REST + admin rotaları)
154
- const routePatterns = [
155
- /post.*role|put.*role|patch.*role|delete.*user/i,
156
- /post.*admin|put.*permission/i,
157
- /\/(api|admin|users?|roles?|permissions?)\/.*(role|admin|permission|promote|elevate|grant|revoke)/i
158
- ];
159
- const hasRoutePattern = routePatterns.some(regex => regex.test(combined));
160
- // 3. Semantic yetki değiştirme desenleri (kod içinde privilege manipülasyonu)
161
- const semanticPatterns = [
162
- /changeRole|assignRole|updateRole|setPermission|grantPermission|revokePermission|makeAdmin|setAdmin/i,
163
- /role\s*=\s*['"`]?(admin|superadmin|super_admin)/i,
164
- /permissions?\s*\.\s*(push|add)/i
165
- ];
166
- const hasSemanticPrivilegeChange = semanticPatterns.some(regex => regex.test(body));
167
- return hasKeyword || hasRoutePattern || hasSemanticPrivilegeChange;
168
- }
169
- function findInterface(sourceFiles, interfaceName) {
170
- for (const sf of sourceFiles) {
171
- const iface = sf.getInterface(interfaceName);
172
- if (iface)
173
- return iface;
174
- }
175
- return undefined;
176
- }
177
- function getInterfaceProperties(iface) {
178
- return iface.getProperties().map((p) => p.getName());
179
- }
180
- // --- Tool Definitions ---
181
- export const governanceTools = [
182
- {
183
- name: "verify_high_risk_compliance",
184
- description: "Analyzes high-risk administrative endpoints and verifies compliance with corporate governance standards (managerApproval, branded types, audit logging, error handling).",
185
- inputSchema: {
186
- type: "object",
187
- properties: {
188
- backendPath: {
189
- type: "string",
190
- description: "Relative path to backend source (default: apps/backend/src)",
191
- },
192
- strictMode: {
193
- type: "boolean",
194
- description: "If true, treats warnings as violations (default: false)",
195
- },
196
- },
197
- },
198
- },
199
- {
200
- name: "detect_contract_drift",
201
- description: "Detects contract drift between backend types and frontend copies, with focus on corporate governance contracts (Branded Types, ManagerApproval, etc.).",
202
- inputSchema: {
203
- type: "object",
204
- properties: {
205
- backendTypesPath: {
206
- type: "string",
207
- description: "Path to backend types directory (default: apps/backend/src/types)",
208
- },
209
- frontendTypesPath: {
210
- type: "string",
211
- description: "Path to frontend type copies (default: apps/web/src/types)",
212
- },
213
- },
214
- },
215
- },
216
- {
217
- name: "validate_branded_types_usage",
218
- description: "Scans the codebase for proper usage of Branded Types as defined in corporate governance standards.",
219
- inputSchema: {
220
- type: "object",
221
- properties: {
222
- targetPath: {
223
- type: "string",
224
- description: "Directory to scan (default: apps/backend/src)",
225
- },
226
- },
227
- },
228
- },
229
- {
230
- name: "audit_governance_compliance",
231
- description: "Performs a high-level governance audit of the entire project by combining results from other governance tools. Returns an overall compliance score and prioritized issues.",
232
- inputSchema: {
233
- type: "object",
234
- properties: {
235
- backendPath: {
236
- type: "string",
237
- description: "Path to backend source (default: apps/backend/src)",
238
- },
239
- includeContractCheck: {
240
- type: "boolean",
241
- description: "Whether to also run contract drift detection (default: true)",
242
- },
243
- },
244
- },
245
- },
246
- ];
247
- // --- Handlers (Implementation starts here) ---
248
- // args is any because these are dynamic MCP tool handlers
249
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
250
- export const governanceHandlers = {
251
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
252
- verify_high_risk_compliance: async (args, projectRoot) => {
253
- const backendPath = args.backendPath || "apps/backend/src";
254
- const fullBackendPath = path.join(projectRoot, backendPath);
255
- if (!fs.existsSync(fullBackendPath)) {
256
- const result = createGovernanceToolResult("verify_high_risk_compliance", createEmptyGovernanceReport(), `Backend source directory not found: ${backendPath}`, false);
257
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
258
- }
259
- const project = createAnalysisProject(projectRoot);
260
- const sourceFiles = collectSourceFiles(fullBackendPath).map(f => project.addSourceFileAtPath(f));
261
- const report = createEmptyGovernanceReport();
262
- // Load high-risk patterns dynamically from corporate governance knowledge
263
- const highRiskKeywords = loadHighRiskPatterns(projectRoot);
264
- let highRiskFunctionsFound = 0;
265
- for (const sourceFile of sourceFiles) {
266
- // Daha kapsamlı fonksiyon + method + route handler tespiti
267
- const functions = [
268
- ...sourceFile.getFunctions(),
269
- ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration),
270
- ...sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction),
271
- ];
272
- for (const func of functions) {
273
- const name = getFunctionLikeName(func);
274
- const bodyText = func.getBodyText() || "";
275
- // === GÜÇLENDİRİLMİŞ HIGH-RISK FONKSİYON TESPİTİ ===
276
- const isLikelyHighRisk = isHighRiskFunction(name, bodyText, highRiskKeywords);
277
- if (!isLikelyHighRisk)
278
- continue;
279
- highRiskFunctionsFound++;
280
- // === Daha Derin ve Yapısal AST Tabanlı Kontroller ===
281
- const parameters = getFunctionLikeParameters(func);
282
- // 1. High-risk validation kontrolü (daha güçlü AST + semantic)
283
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
284
- const hasProperHighRiskCheck = func.getDescendantsOfKind(SyntaxKind.IfStatement).some((ifStmt) => {
285
- const conditionText = ifStmt.getExpression().getText().toLowerCase();
286
- const hasFlag = conditionText.includes("ishighriskadminaction");
287
- const hasApproval = conditionText.includes("managerapproval");
288
- return hasFlag && hasApproval;
289
- });
290
- if (!hasProperHighRiskCheck) {
291
- report.violations.push({
292
- file: path.relative(projectRoot, sourceFile.getFilePath()),
293
- line: func.getStartLineNumber(),
294
- rule: "insufficient_high_risk_validation",
295
- severity: "critical",
296
- message: `High-risk function "${name}" does not contain proper conditional check for both isHighRiskAdminAction and managerApproval.`,
297
- suggestion: "Add: if (!body.isHighRiskAdminAction || !body.managerApproval) { throw new HighRiskActionRequiresApprovalError(...) }"
298
- });
299
- }
300
- else {
301
- // Ekstra: ManagerApproval yapısal validasyonu (approvedBy kontrolü)
302
- const hasApprovedByCheck = /approvedBy|approval\.approvedBy/i.test(bodyText);
303
- if (!hasApprovedByCheck) {
304
- report.violations.push({
305
- file: path.relative(projectRoot, sourceFile.getFilePath()),
306
- line: func.getStartLineNumber(),
307
- rule: "weak_manager_approval_validation",
308
- severity: "high",
309
- message: `High-risk function "${name}" checks managerApproval but does not validate approvedBy field.`,
310
- suggestion: "Add check: if (managerApproval.approvedBy !== 'manager') { ... }"
311
- });
312
- }
313
- }
314
- // 2. Typed Error kullanımı (daha doğru tespit)
315
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
316
- const throwsTypedError = func.getDescendantsOfKind(SyntaxKind.ThrowStatement).some((throwStmt) => {
317
- const expr = throwStmt.getExpression().getText();
318
- return expr.includes("HighRiskActionRequiresApprovalError") || expr.includes("DomainError");
319
- });
320
- if (!throwsTypedError && hasProperHighRiskCheck) {
321
- report.violations.push({
322
- file: path.relative(projectRoot, sourceFile.getFilePath()),
323
- line: func.getStartLineNumber(),
324
- rule: "missing_typed_error",
325
- severity: "high",
326
- message: `High-risk function "${name}" performs governance checks but does not throw a typed DomainError.`,
327
- suggestion: "Throw HighRiskActionRequiresApprovalError for high-risk governance violations."
328
- });
329
- }
330
- // 3. Audit Logging kontrolü (daha iyi tespit)
331
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
332
- const hasAuditLogging = func.getDescendantsOfKind(SyntaxKind.CallExpression).some((call) => {
333
- const callText = call.getExpression().getText().toLowerCase();
334
- return callText.includes("audit") || callText.includes("log");
335
- }) || /auditLog|audit\.create/i.test(bodyText);
336
- if (!hasAuditLogging) {
337
- report.violations.push({
338
- file: path.relative(projectRoot, sourceFile.getFilePath()),
339
- line: func.getStartLineNumber(),
340
- rule: "missing_audit_logging",
341
- severity: "high",
342
- message: `High-risk function "${name}" does not create an audit log entry.`,
343
- suggestion: "Always create an audit log entry for high-risk actions (see audit-logging-standard.md)."
344
- });
345
- }
346
- // 4. Branded Types kullanımı (parametre seviyesinde gerçek tip kontrolü)
347
- let hasBrandedTypesInParams = false;
348
- for (const param of parameters) {
349
- const typeText = param.getType().getText();
350
- if (/\b(UserID|RoleID|ApprovalID|TraceID)\b/.test(typeText)) {
351
- hasBrandedTypesInParams = true;
352
- break;
353
- }
354
- }
355
- if (!hasBrandedTypesInParams) {
356
- report.violations.push({
357
- file: path.relative(projectRoot, sourceFile.getFilePath()),
358
- line: func.getStartLineNumber(),
359
- rule: "missing_branded_types",
360
- severity: "high",
361
- message: `High-risk function "${name}" does not use Branded Types in its parameters.`,
362
- suggestion: "Use UserID, RoleID, ApprovalID etc. instead of plain string or number."
363
- });
364
- }
365
- }
366
- }
367
- // Her high-risk fonksiyon için şu kontroller yapılıyor:
368
- // 1. High-risk flag + managerApproval kontrolü
369
- // 2. Typed DomainError (HighRiskActionRequiresApprovalError) kullanımı
370
- // 3. Audit logging çağrısı
371
- // 4. Branded Types kullanımı (parametrelerde)
372
- const totalChecks = highRiskFunctionsFound * 4;
373
- const violationsCount = report.violations.length;
374
- const passed = Math.max(0, totalChecks - violationsCount);
375
- report.total_checks = totalChecks;
376
- report.passed_checks = passed;
377
- report.compliance_score = totalChecks > 0
378
- ? Math.round((passed / totalChecks) * 100)
379
- : 100;
380
- if (violationsCount === 0 && highRiskFunctionsFound > 0) {
381
- report.summary = `✅ Good compliance. Analyzed ${highRiskFunctionsFound} high-risk functions.`;
382
- }
383
- else if (highRiskFunctionsFound === 0) {
384
- report.summary = "No high-risk functions matching governance patterns were detected.";
385
- }
386
- else {
387
- report.summary = `Analyzed ${highRiskFunctionsFound} high-risk functions. Found ${violationsCount} governance violations.`;
388
- }
389
- if (violationsCount > 0) {
390
- report.recommendations.push("Review all critical violations first (missing managerApproval checks).");
391
- report.recommendations.push("Load corporate-governance/high-risk-actions-managerapproval.md for the exact expected pattern.");
392
- }
393
- const finalResult = createGovernanceToolResult("verify_high_risk_compliance", report, report.summary || "High-risk compliance analysis completed.");
394
- return {
395
- content: [{
396
- type: "text",
397
- text: JSON.stringify(finalResult, null, 2)
398
- }]
399
- };
400
- },
401
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
402
- detect_contract_drift: async (args, projectRoot) => {
403
- const backendTypesPath = args.backendTypesPath || "apps/backend/src/types";
404
- const frontendTypesPath = args.frontendTypesPath || "apps/web/src/types";
405
- const backendDir = path.join(projectRoot, backendTypesPath);
406
- const frontendDir = path.join(projectRoot, frontendTypesPath);
407
- if (!fs.existsSync(backendDir)) {
408
- const result = createGovernanceToolResult("detect_contract_drift", { drift_detected: false }, `Backend types directory not found: ${backendTypesPath}`, false);
409
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
410
- }
411
- const project = createAnalysisProject(projectRoot);
412
- // Load backend types
413
- const backendFiles = collectTypeFiles(backendDir);
414
- const backendSourceFiles = backendFiles.map(f => project.addSourceFileAtPath(f));
415
- // Load frontend type copies (if exists)
416
- let frontendSourceFiles = [];
417
- if (fs.existsSync(frontendDir)) {
418
- const frontendFiles = collectTypeFiles(frontendDir);
419
- frontendSourceFiles = frontendFiles.map(f => project.addSourceFileAtPath(f));
420
- }
421
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
422
- const drifts = [];
423
- // Compare interfaces (focus on governance-related types)
424
- const governanceTypeNames = [
425
- "UserID", "RoleID", "ApprovalID", "TraceID",
426
- "ManagerApproval", "HighRiskActionPayload",
427
- "User", "Role", "DomainError"
428
- ];
429
- for (const typeName of governanceTypeNames) {
430
- const backendInterface = findInterface(backendSourceFiles, typeName);
431
- const frontendInterface = findInterface(frontendSourceFiles, typeName);
432
- if (backendInterface && !frontendInterface) {
433
- drifts.push({
434
- type: typeName,
435
- issue: "MISSING_IN_FRONTEND",
436
- severity: "high",
437
- message: `Governance type "${typeName}" exists in backend but is missing in frontend copies.`,
438
- });
439
- }
440
- if (backendInterface && frontendInterface) {
441
- const backendProps = getInterfaceProperties(backendInterface);
442
- const frontendProps = getInterfaceProperties(frontendInterface);
443
- const missingInFrontend = backendProps.filter(p => !frontendProps.includes(p));
444
- const extraInFrontend = frontendProps.filter(p => !backendProps.includes(p));
445
- if (missingInFrontend.length > 0 || extraInFrontend.length > 0) {
446
- drifts.push({
447
- type: typeName,
448
- issue: "STRUCTURE_MISMATCH",
449
- severity: "high",
450
- missing_in_frontend: missingInFrontend,
451
- extra_in_frontend: extraInFrontend,
452
- message: `Structural drift detected in governance type "${typeName}".`,
453
- });
454
- }
455
- }
456
- }
457
- const report = {
458
- drift_detected: drifts.length > 0,
459
- total_governance_types_checked: governanceTypeNames.length,
460
- drifts,
461
- backend_types_path: backendTypesPath,
462
- frontend_types_path: frontendTypesPath,
463
- recommendation: drifts.length > 0
464
- ? "Run update_contract_hash after aligning frontend copies. Consider using corporate-governance-playbook.md for alignment process."
465
- : "No significant drift detected in core governance types.",
466
- };
467
- const summary = drifts.length > 0
468
- ? `Contract drift detected in ${drifts.length} governance types.`
469
- : "No significant contract drift found in core governance types.";
470
- const finalResult = createGovernanceToolResult("detect_contract_drift", report, summary);
471
- return {
472
- content: [{
473
- type: "text",
474
- text: JSON.stringify(finalResult, null, 2)
475
- }]
476
- };
477
- },
478
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
479
- validate_branded_types_usage: async (args, projectRoot) => {
480
- const targetPath = args.targetPath || "apps/backend/src";
481
- const fullPath = path.join(projectRoot, targetPath);
482
- const report = {
483
- total_files_scanned: 0,
484
- branded_types_found: 0,
485
- plain_id_violations: [],
486
- summary: "",
487
- recommendations: [],
488
- };
489
- if (!fs.existsSync(fullPath)) {
490
- const result = createGovernanceToolResult("validate_branded_types_usage", { total_files_scanned: 0, plain_id_violations: [] }, `Target directory not found: ${targetPath}`, false);
491
- return { content: [{ type: "text", text: JSON.stringify(result, null, 2) }] };
492
- }
493
- const project = createAnalysisProject(projectRoot);
494
- const sourceFiles = collectSourceFiles(fullPath).map(f => project.addSourceFileAtPath(f));
495
- report.total_files_scanned = sourceFiles.length;
496
- // Branded type names we care about (from our governance standards)
497
- const brandedTypeNames = ["UserID", "RoleID", "ApprovalID", "TraceID"];
498
- for (const sourceFile of sourceFiles) {
499
- // Check interface/type definitions first
500
- const interfaces = sourceFile.getInterfaces();
501
- for (const iface of interfaces) {
502
- for (const prop of iface.getProperties()) {
503
- const typeText = prop.getType().getText();
504
- const propName = prop.getName().toLowerCase();
505
- // Look for ID fields that should be branded
506
- if (propName.includes("id") || propName.includes("userid") || propName.includes("roleid")) {
507
- const isBranded = brandedTypeNames.some(bt => typeText.includes(bt));
508
- if (!isBranded && (typeText.includes("string") || typeText.includes("number"))) {
509
- report.plain_id_violations.push({
510
- file: path.relative(projectRoot, sourceFile.getFilePath()),
511
- line: prop.getStartLineNumber(),
512
- issue: "plain_id_in_interface",
513
- message: `Property "${prop.getName()}" uses plain ${typeText} instead of a branded type.`,
514
- });
515
- }
516
- }
517
- }
518
- }
519
- // Check function parameters and variables
520
- const functions = [
521
- ...sourceFile.getFunctions(),
522
- ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration),
523
- ];
524
- for (const func of functions) {
525
- // Parameters
526
- for (const param of getFunctionLikeParameters(func)) {
527
- const typeText = param.getType().getText();
528
- const paramName = param.getName().toLowerCase();
529
- if (paramName.includes("id") || paramName.includes("userid") || paramName.includes("roleid")) {
530
- const isBranded = brandedTypeNames.some(bt => typeText.includes(bt));
531
- if (!isBranded && (typeText.includes("string") || typeText.includes("number"))) {
532
- report.plain_id_violations.push({
533
- file: path.relative(projectRoot, sourceFile.getFilePath()),
534
- line: param.getStartLineNumber(),
535
- issue: "plain_id_in_parameter",
536
- message: `Parameter "${param.getName()}" uses plain ${typeText} instead of branded type (e.g. UserID).`,
537
- });
538
- }
539
- else if (isBranded) {
540
- report.branded_types_found++;
541
- }
542
- }
543
- }
544
- }
545
- }
546
- const violationCount = report.plain_id_violations.length;
547
- if (violationCount === 0) {
548
- report.summary = `✅ Good. No plain ID usages found in governance-related contexts across ${report.total_files_scanned} files.`;
549
- }
550
- else {
551
- report.summary = `Found ${violationCount} violations where plain string/number was used instead of branded types.`;
552
- report.recommendations.push("Replace plain IDs with branded types (UserID, RoleID, ApprovalID, etc.).");
553
- report.recommendations.push("See branded-types-law.md for the full standard.");
554
- }
555
- const summary = violationCount === 0
556
- ? `No branded type violations found in ${report.total_files_scanned} files.`
557
- : `${violationCount} plain ID violations detected.`;
558
- const finalResult = createGovernanceToolResult("validate_branded_types_usage", report, summary);
559
- return {
560
- content: [{
561
- type: "text",
562
- text: JSON.stringify(finalResult, null, 2)
563
- }]
564
- };
565
- },
566
- // ============================================
567
- // audit_governance_compliance (Bağımsız Versiyon)
568
- // ============================================
569
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
570
- audit_governance_compliance: async (args, projectRoot) => {
571
- const backendPath = args.backendPath || "apps/backend/src";
572
- const includeContractCheck = args.includeContractCheck ?? true;
573
- const fullBackendPath = path.join(projectRoot, backendPath);
574
- const frontendTypesPath = path.join(projectRoot, "apps/web/src/types");
575
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
576
- const overview = {
577
- overall_score: 0,
578
- areas: {},
579
- critical_issues: [],
580
- summary: "",
581
- recommendations: [],
582
- };
583
- // ============================================
584
- // 1. High-Risk Compliance (Bağımsız basit analiz)
585
- // ============================================
586
- let highRiskViolations = 0;
587
- if (fs.existsSync(fullBackendPath)) {
588
- const project = createAnalysisProject(projectRoot);
589
- const sourceFiles = collectSourceFiles(fullBackendPath).map(f => project.addSourceFileAtPath(f));
590
- const highRiskKeywords = ["promote", "elevate", "superadmin", "admin", "role", "delete", "purge", "reset"];
591
- for (const sourceFile of sourceFiles) {
592
- const functions = [
593
- ...sourceFile.getFunctions(),
594
- ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration),
595
- ...sourceFile.getDescendantsOfKind(SyntaxKind.ArrowFunction),
596
- ];
597
- for (const func of functions) {
598
- const name = getFunctionLikeName(func);
599
- const bodyText = func.getBodyText() || "";
600
- const lowerBody = bodyText.toLowerCase();
601
- const isHighRisk = highRiskKeywords.some(kw => name.toLowerCase().includes(kw) || lowerBody.includes(kw));
602
- if (isHighRisk) {
603
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
604
- const hasFlagAndApproval = func.getDescendantsOfKind(SyntaxKind.IfStatement).some((ifStmt) => {
605
- const cond = ifStmt.getExpression().getText().toLowerCase();
606
- return cond.includes("ishighriskadminaction") && cond.includes("managerapproval");
607
- });
608
- if (!hasFlagAndApproval)
609
- highRiskViolations++;
610
- }
611
- }
612
- }
613
- }
614
- overview.areas.high_risk = {
615
- violations: highRiskViolations,
616
- status: highRiskViolations === 0 ? "good" : "issues_found",
617
- };
618
- // ============================================
619
- // 2. Branded Types (Bağımsız basit analiz)
620
- // ============================================
621
- let brandedViolations = 0;
622
- if (fs.existsSync(fullBackendPath)) {
623
- const project = createAnalysisProject(projectRoot);
624
- const sourceFiles = collectSourceFiles(fullBackendPath).map(f => project.addSourceFileAtPath(f));
625
- const brandedNames = ["UserID", "RoleID", "ApprovalID", "TraceID"];
626
- for (const sourceFile of sourceFiles) {
627
- const functions = [
628
- ...sourceFile.getFunctions(),
629
- ...sourceFile.getDescendantsOfKind(SyntaxKind.MethodDeclaration),
630
- ];
631
- for (const func of functions) {
632
- const params = func.getParameters();
633
- for (const param of params) {
634
- const typeText = param.getType().getText();
635
- const paramName = param.getName().toLowerCase();
636
- if ((paramName.includes("id") || paramName.includes("userid") || paramName.includes("roleid")) &&
637
- !brandedNames.some(b => typeText.includes(b)) &&
638
- (typeText.includes("string") || typeText.includes("number"))) {
639
- brandedViolations++;
640
- }
641
- }
642
- }
643
- }
644
- }
645
- overview.areas.branded_types = {
646
- violations: brandedViolations,
647
- status: brandedViolations === 0 ? "good" : "issues_found",
648
- };
649
- // ============================================
650
- // 3. Contract Drift (basit klasör karşılaştırması)
651
- // ============================================
652
- if (includeContractCheck) {
653
- const backendTypesExist = fs.existsSync(path.join(projectRoot, "apps/backend/src/types"));
654
- const frontendTypesExist = fs.existsSync(frontendTypesPath);
655
- overview.areas.contract_drift = {
656
- backend_types_present: backendTypesExist,
657
- frontend_types_present: frontendTypesExist,
658
- likely_drift: backendTypesExist && !frontendTypesExist,
659
- };
660
- }
661
- // ============================================
662
- // Genel Skor Hesaplama
663
- // ============================================
664
- const highRiskScore = overview.areas.high_risk.violations === 0 ? 85 : 55;
665
- const brandedScore = overview.areas.branded_types.violations === 0 ? 90 : 60;
666
- const contractScore = overview.areas.contract_drift?.likely_drift === false ? 80 : 50;
667
- const scores = [highRiskScore, brandedScore, contractScore].filter(Boolean);
668
- overview.overall_score = Math.round(scores.reduce((a, b) => a + b, 0) / scores.length);
669
- // Kritik sorunları topla
670
- if (overview.areas.high_risk.violations > 0) {
671
- overview.critical_issues.push({
672
- area: "high_risk",
673
- count: overview.areas.high_risk.violations,
674
- message: "High-risk functions without proper managerApproval validation detected.",
675
- });
676
- }
677
- if (overview.areas.branded_types.violations > 0) {
678
- overview.critical_issues.push({
679
- area: "branded_types",
680
- count: overview.areas.branded_types.violations,
681
- message: "Plain string/number IDs used instead of Branded Types.",
682
- });
683
- }
684
- overview.summary = `Overall governance compliance score: ${overview.overall_score}/100 (Independent analysis)`;
685
- if (overview.overall_score < 70) {
686
- overview.recommendations.push("Focus on high-risk validation and Branded Types first.");
687
- }
688
- const finalResult = createGovernanceToolResult("audit_governance_compliance", overview, overview.summary);
689
- return {
690
- content: [{
691
- type: "text",
692
- text: JSON.stringify(finalResult, null, 2)
693
- }]
694
- };
695
- },
696
- };