deepdebug-local-agent 1.0.23 → 1.2.0

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 (178) hide show
  1. package/.claude/context.md +76 -0
  2. package/.claude/skills/README.md +25 -0
  3. package/.claude/skills/tdd-debugging/SKILL.md +53 -0
  4. package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
  5. package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
  6. package/.claude/standards/README.md +51 -0
  7. package/.claude/standards/clean-code.md +75 -0
  8. package/.claude/standards/code-review-checklist.md +61 -0
  9. package/.claude/standards/layered-architecture.md +163 -0
  10. package/.claude/standards/node.md +45 -0
  11. package/.claude/standards/security-owasp.md +114 -0
  12. package/.claude/standards/simplicity.md +42 -0
  13. package/.claude/standards/solid.md +64 -0
  14. package/.claude/standards/testing.md +59 -0
  15. package/.cursor/rules/standards.mdc +17 -0
  16. package/.github/workflows/build-byoc-image.yml +315 -0
  17. package/.github/workflows/deploy-gcp.yml +102 -0
  18. package/.github/workflows/npm-publish.yml +76 -8
  19. package/AGENTS.md +21 -0
  20. package/CHANGELOG.md +76 -0
  21. package/CLAUDE.md +13 -0
  22. package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
  23. package/Dockerfile +4 -0
  24. package/Dockerfile.full +55 -3
  25. package/bin/install.js +16 -5
  26. package/cloudbuild-full.yaml +57 -0
  27. package/docker-compose.yml +5 -3
  28. package/docs/OBSERVABILITY.md +164 -0
  29. package/docs/byoc-agent-image.md +70 -0
  30. package/docs/dlp.md +78 -0
  31. package/docs/graphify-packaging-plan.md +133 -0
  32. package/docs/graphify-validation-run-2026-10-28.md +243 -0
  33. package/docs/releasing.md +37 -0
  34. package/docs/signed-commands.md +163 -0
  35. package/env.example +34 -0
  36. package/helm/Chart.yaml +2 -2
  37. package/helm/templates/deployment.yaml +13 -3
  38. package/helm/templates/networkpolicy.yaml +22 -0
  39. package/helm/templates/secret.yaml +1 -0
  40. package/helm/values.yaml +13 -14
  41. package/mcp-server.js +4 -1
  42. package/package.json +9 -4
  43. package/scripts/next-version.mjs +75 -0
  44. package/scripts/upgrade-observability-push.sh +227 -0
  45. package/src/ai-engine.js +22 -22
  46. package/src/config/trusted-signing-keys.json +3 -0
  47. package/src/detectors/build-tool-detector.js +50 -0
  48. package/src/detectors/multi-project-detector.js +22 -22
  49. package/src/detectors/service-detector.js +24 -24
  50. package/src/dlp/engine.js +111 -0
  51. package/src/dlp/index.js +137 -0
  52. package/src/dlp/rules.js +97 -0
  53. package/src/dlp/validators.js +123 -0
  54. package/src/exec-utils.js +26 -4
  55. package/src/git/base-git-provider.js +117 -117
  56. package/src/git/bitbucket-provider.js +2 -2
  57. package/src/git/git-provider-registry.js +18 -18
  58. package/src/git/github-provider.js +13 -13
  59. package/src/git/multi-repo-pr.js +161 -0
  60. package/src/mcp-http-server.js +32 -16
  61. package/src/observability/bundle-files.js +418 -0
  62. package/src/observability/detect.js +151 -0
  63. package/src/observability/provision.js +212 -0
  64. package/src/observability/render-cli.js +59 -0
  65. package/src/observability/routes.js +88 -0
  66. package/src/patch/patch-engine.js +28 -28
  67. package/src/patch-manager.js +85 -85
  68. package/src/patch_bkp.js +14 -14
  69. package/src/ports.js +4 -4
  70. package/src/routes/workspace.route.js +47 -16
  71. package/src/runtimes/base-runtime.js +75 -75
  72. package/src/runtimes/java/gradle-runtime.js +17 -17
  73. package/src/runtimes/java/java-integrations.js +25 -25
  74. package/src/runtimes/java/maven-runtime.js +34 -34
  75. package/src/runtimes/node/node-integrations.js +1 -1
  76. package/src/runtimes/node/npm-runtime.js +14 -14
  77. package/src/runtimes/node/yarn-runtime.js +4 -4
  78. package/src/runtimes/runtime-registry.js +25 -25
  79. package/src/server-local.js +4 -4
  80. package/src/server.js +1366 -333
  81. package/src/siblings/burst-suppressor.js +89 -0
  82. package/src/siblings/capabilities.js +273 -0
  83. package/src/siblings/env-compat.cjs +58 -0
  84. package/src/siblings/incident-forwarder.js +322 -0
  85. package/src/siblings/logger.js +15 -0
  86. package/src/siblings/metrics.js +94 -0
  87. package/src/utils/agent-auth.js +184 -0
  88. package/src/utils/approval-token.js +56 -0
  89. package/src/utils/bind-host.js +29 -0
  90. package/src/utils/branch-refspec.js +98 -0
  91. package/src/utils/child-env.js +34 -0
  92. package/src/utils/environment-diagnostics.js +55 -55
  93. package/src/utils/exec-policy.js +962 -0
  94. package/src/utils/exec-utils.js +15 -15
  95. package/src/utils/file-count.js +46 -0
  96. package/src/utils/fs-utils.js +10 -10
  97. package/src/utils/multi-repo-open.js +132 -0
  98. package/src/utils/open-gate.js +183 -0
  99. package/src/utils/ranged-read.js +65 -0
  100. package/src/utils/request-guards.js +77 -0
  101. package/src/utils/request-signing.js +342 -0
  102. package/src/utils/root-recovery.js +244 -0
  103. package/src/utils/safe-exec-args.js +58 -0
  104. package/src/utils/sandbox.js +182 -0
  105. package/src/utils/search-outcome.js +56 -0
  106. package/src/utils/workspace-root.js +80 -0
  107. package/src/validators/syntax-validator.js +149 -0
  108. package/src/workspace/detect-port.js +16 -16
  109. package/src/workspace/file-reader.js +1 -1
  110. package/src/workspace/process-manager.js +46 -46
  111. package/src/workspace-manager.js +86 -24
  112. package/test/adapters/registry.test.js +9 -2
  113. package/test/agent-auth.test.js +194 -0
  114. package/test/bind-host.test.js +26 -0
  115. package/test/branch-refspec.test.js +224 -0
  116. package/test/burst-suppressor.test.js +60 -0
  117. package/test/capabilities-access.test.js +47 -0
  118. package/test/capabilities.test.js +87 -0
  119. package/test/child-env.test.js +23 -0
  120. package/test/dlp/engine.test.js +173 -0
  121. package/test/dlp/fixtures.js +126 -0
  122. package/test/dlp/index.test.js +166 -0
  123. package/test/env-compat.test.js +71 -0
  124. package/test/exec-policy.test.js +463 -0
  125. package/test/file-count.test.js +64 -0
  126. package/test/incident-forwarder.test.js +231 -0
  127. package/test/multi-repo-open.test.js +180 -0
  128. package/test/multi-repo-pr.test.js +129 -0
  129. package/test/next-version.test.js +48 -0
  130. package/test/observability-detect.test.js +199 -0
  131. package/test/open-gate.test.js +341 -0
  132. package/test/ranged-read.test.js +60 -0
  133. package/test/request-guards.test.js +91 -0
  134. package/test/request-signing.test.js +284 -0
  135. package/test/root-recovery-sidecar.test.js +171 -0
  136. package/test/root-recovery.test.js +274 -0
  137. package/test/safe-exec-args.test.js +58 -0
  138. package/test/sandbox.test.js +98 -0
  139. package/test/search-outcome.test.js +50 -0
  140. package/test/workspace-root.test.js +64 -0
  141. package/tools/graphify-validation/README.md +92 -0
  142. package/tools/graphify-validation/config/questions.json +31 -0
  143. package/tools/graphify-validation/config/repos.json +9 -0
  144. package/tools/graphify-validation/config/symbols.json +6 -0
  145. package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
  146. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
  147. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
  148. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
  149. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
  150. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
  151. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
  152. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
  153. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
  154. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
  155. package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
  156. package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
  157. package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
  158. package/tools/graphify-validation/lib/graph_metrics.py +96 -0
  159. package/tools/graphify-validation/lib/gv_common.py +130 -0
  160. package/tools/graphify-validation/lib/licenses.py +47 -0
  161. package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
  162. package/tools/graphify-validation/lib/recall.py +115 -0
  163. package/tools/graphify-validation/lib/report.py +226 -0
  164. package/tools/graphify-validation/lib/run_all.py +237 -0
  165. package/tools/graphify-validation/lib/run_graphify.py +144 -0
  166. package/tools/graphify-validation/lib/rusage_helper.py +17 -0
  167. package/tools/graphify-validation/lib/sample_edges.py +246 -0
  168. package/tools/graphify-validation/lib/spring_fixture.py +81 -0
  169. package/tools/graphify-validation/requirements-py311.lock +508 -0
  170. package/tools/graphify-validation/requirements-tools.in +1 -0
  171. package/tools/graphify-validation/requirements-tools.lock +371 -0
  172. package/tools/graphify-validation/requirements.in +1 -0
  173. package/tools/graphify-validation/requirements.lock +502 -0
  174. package/tools/graphify-validation/run.sh +43 -0
  175. package/tools/graphify-validation/tests/test_harness.py +151 -0
  176. package/cloudbuild-agent-qa.yaml +0 -43
  177. package/cloudbuild-agent-staging.yaml +0 -40
  178. package/cloudbuild.yaml.deprecated +0 -42
@@ -0,0 +1,29 @@
1
+ /**
2
+ * bind-host.js — which interface the agent's HTTP server listens on.
3
+ *
4
+ * Fail-closed default (security review 2026-10-11, C1): the agent only listens
5
+ * on all interfaces (0.0.0.0) when it is meant to be reached remotely, i.e. the
6
+ * auth mode is `token` (LOCAL_AGENT_TOKEN / AGENT_SHARED_SECRET), Cloud Run
7
+ * `compat` (IAM is the perimeter) or an explicit opt-out. Otherwise (the
8
+ * implicit `loopback` mode) it binds 127.0.0.1, so the port is not even open
9
+ * to the LAN. LOCAL_AGENT_BIND_HOST always wins. BYOC Azure (ACI)
10
+ * container groups put a TLS-terminating Caddy sidecar in front of the agent
11
+ * and set LOCAL_AGENT_BIND_HOST=127.0.0.1 so the agent is reachable only
12
+ * through the sidecar (containers in an ACI group share localhost).
13
+ */
14
+ import net from "node:net";
15
+
16
+ export const DEFAULT_BIND_HOST = "0.0.0.0";
17
+ export const LOOPBACK_BIND_HOST = "127.0.0.1";
18
+
19
+ /** @param authCfg result of resolveAuthConfig(); without it the safe loopback default applies. */
20
+ export function resolveBindHost(env = process.env, authCfg = null) {
21
+ const raw = (env.LOCAL_AGENT_BIND_HOST || "").trim();
22
+ const remoteCapable = Boolean(authCfg) && ["token", "compat", "off"].includes(authCfg.mode);
23
+ const fallback = remoteCapable ? DEFAULT_BIND_HOST : LOOPBACK_BIND_HOST;
24
+ if (!raw) return fallback;
25
+ if (raw === "localhost") return "127.0.0.1";
26
+ if (net.isIP(raw)) return raw;
27
+ console.warn(`[bind-host] ignoring invalid LOCAL_AGENT_BIND_HOST=${JSON.stringify(raw)}; using ${fallback}`);
28
+ return fallback;
29
+ }
@@ -0,0 +1,98 @@
1
+ /**
2
+ * BRANCH REFSPEC FIX - explicit ref fetch for /workspace/open REUSE paths.
3
+ *
4
+ * Problem (proven in QA, 2026-07-20, agent rev 00021-kd8): the initial clone
5
+ * uses `git clone --branch <b> --single-branch`, which restricts the remote
6
+ * refspec to that ONE branch. When a LATER /workspace/open of the SAME repo
7
+ * asks for a DIFFERENT branch (e.g. the auto-review of a PR branch created
8
+ * minutes earlier), the reuse path runs `git fetch origin` (which only
9
+ * fetches the restricted refspec), swallows the checkout error, and dies on
10
+ * `git reset --hard origin/<branch>` with "fatal: ambiguous argument ...
11
+ * unknown revision" -> HTTP 500 -> review FAILED (PR #13 row in
12
+ * ai_pr_reviews). The fix: BEFORE checkout/reset, fetch the requested ref
13
+ * explicitly with `+refs/heads/<b>:refs/remotes/origin/<b>` so single-branch
14
+ * clones can see branches born after the original clone.
15
+ *
16
+ * Kept pure/dependency-injected so guard tests can pin every decision
17
+ * without a running server (same style as utils/open-gate.js).
18
+ */
19
+
20
+ /**
21
+ * Shell-safety + git-ref sanity predicate for a requested branch name.
22
+ * The reuse path interpolates the branch into a shell command line, so we
23
+ * fail CLOSED on anything outside a conservative charset (no quotes, no
24
+ * whitespace, no $ ` ; & | > < ( ) { } \ ~ ^ : ? * [ @{ ), plus the git
25
+ * ref rules that matter here (no "..", no "//", no leading "-" or ".",
26
+ * no trailing "/" or ".lock").
27
+ */
28
+ export function isSafeBranchName(branch) {
29
+ if (typeof branch !== "string") return false;
30
+ const b = branch.trim();
31
+ if (!b || b.length > 255) return false;
32
+ if (!/^[A-Za-z0-9][A-Za-z0-9._/-]*$/.test(b)) return false;
33
+ if (b.includes("..") || b.includes("//")) return false;
34
+ if (b.endsWith("/") || b.endsWith(".lock")) return false;
35
+ return true;
36
+ }
37
+
38
+ /**
39
+ * The explicit refspec that makes a single-branch clone see the requested
40
+ * branch: +refs/heads/<b>:refs/remotes/origin/<b>. Caller MUST validate the
41
+ * branch with isSafeBranchName first; this builder throws on unsafe input
42
+ * as defense in depth (never silently emits an injectable string).
43
+ */
44
+ export function explicitBranchRefspec(branch) {
45
+ if (!isSafeBranchName(branch)) {
46
+ throw new Error(`unsafe branch name for refspec: ${String(branch)}`);
47
+ }
48
+ const b = branch.trim();
49
+ return `+refs/heads/${b}:refs/remotes/origin/${b}`;
50
+ }
51
+
52
+ /**
53
+ * Does a `git fetch origin <refspec>` failure mean the branch does not
54
+ * exist on the remote (as opposed to auth/network/other errors)?
55
+ * git says: "fatal: couldn't find remote ref refs/heads/<b>" and clones say
56
+ * "Remote branch <b> not found in upstream origin".
57
+ */
58
+ export function isRemoteBranchMissing(message) {
59
+ if (typeof message !== "string" || !message) return false;
60
+ const m = message.toLowerCase();
61
+ return m.includes("couldn't find remote ref")
62
+ || m.includes("could not find remote ref")
63
+ || (m.includes("remote branch") && m.includes("not found"));
64
+ }
65
+
66
+ /**
67
+ * Fetch the requested ref explicitly on a REUSE path, with an injectable
68
+ * exec so guard tests can pin the exact command without git or a server.
69
+ *
70
+ * Throws tagged errors the HTTP handlers translate into CLEAR responses
71
+ * (never a generic 500):
72
+ * .code = "INVALID_BRANCH" .httpStatus = 400 (shell-unsafe branch name)
73
+ * .code = "BRANCH_NOT_FOUND" .httpStatus = 404 (branch absent on remote)
74
+ * Any other fetch failure is rethrown untouched (existing catch semantics).
75
+ *
76
+ * @param {(cmd:string, opts:object)=>Promise<any>} execFn e.g. execAsync
77
+ * @param {string} branch requested branch
78
+ * @param {string} cwd repo dir on disk
79
+ */
80
+ export async function fetchRequestedRef(execFn, branch, cwd) {
81
+ if (!isSafeBranchName(branch)) {
82
+ const err = new Error(`invalid branch name: ${String(branch)}`);
83
+ err.code = "INVALID_BRANCH";
84
+ err.httpStatus = 400;
85
+ throw err;
86
+ }
87
+ try {
88
+ await execFn(`git fetch origin "${explicitBranchRefspec(branch)}"`, { cwd, timeout: 120000 });
89
+ } catch (fetchErr) {
90
+ if (isRemoteBranchMissing(fetchErr && fetchErr.message)) {
91
+ const err = new Error(`branch not found on remote: ${branch.trim()}`);
92
+ err.code = "BRANCH_NOT_FOUND";
93
+ err.httpStatus = 404;
94
+ throw err;
95
+ }
96
+ throw fetchErr;
97
+ }
98
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * child-env.js — environment handed to commands the MODEL asked us to run
3
+ * (/workspace/exec, /setup/run-command, /mcp/execute-command).
4
+ *
5
+ * WHY. Those routes spawned `{ ...process.env }`, so a command such as `env`
6
+ * or `cat /proc/self/environ` (or any prompt-injected one) read the agent's
7
+ * own credentials: AUTH_TOKEN / DEEPDEBUG_API_KEY (gateway), AGENT_SHARED_SECRET
8
+ * / LOCAL_AGENT_TOKEN (this API), git tokens. The child gets everything else
9
+ * (PATH, HOME, JAVA_HOME, proxies...) so builds and tests still work.
10
+ */
11
+
12
+ const SECRET_NAME = /(TOKEN|SECRET|PASSWORD|PASSWD|API[_-]?KEY|APIKEY|CREDENTIAL|PRIVATE[_-]?KEY|SIGNING[_-]?KEY)/i;
13
+
14
+ /** Names that match the pattern but carry no secret and are needed by git/tools. */
15
+ const KEEP = new Set([
16
+ "GIT_TERMINAL_PROMPT", "GIT_ASKPASS", "GIT_SSH_COMMAND", "SSH_AUTH_SOCK",
17
+ ]);
18
+
19
+ export function isSecretEnvName(name) {
20
+ return !KEEP.has(name) && SECRET_NAME.test(String(name));
21
+ }
22
+
23
+ /**
24
+ * Copy of `env` without secret-looking variables, plus `extra` (which is never
25
+ * filtered: the caller is explicit about what it adds).
26
+ */
27
+ export function sanitizedChildEnv(env = process.env, extra = {}) {
28
+ const out = {};
29
+ for (const [k, v] of Object.entries(env)) {
30
+ if (v === undefined || isSecretEnvName(k)) continue;
31
+ out[k] = v;
32
+ }
33
+ return { ...out, ...extra };
34
+ }
@@ -8,22 +8,22 @@ const execAsync = promisify(exec);
8
8
  /**
9
9
  * EnvironmentDiagnostics
10
10
  *
11
- * Detecta automaticamente ferramentas de desenvolvimento instaladas no sistema.
12
- * Útil para debugging e setup do ambiente.
11
+ * Automatically detects development tools installed on the system.
12
+ * Useful for debugging and environment setup.
13
13
  */
14
14
  export class EnvironmentDiagnostics {
15
15
 
16
16
  constructor() {
17
17
  this.cache = null;
18
18
  this.cacheTime = 0;
19
- this.cacheTTL = 60000; // 1 minuto
19
+ this.cacheTTL = 60000; // 1 minute
20
20
  }
21
21
 
22
22
  /**
23
- * Executa diagnóstico completo do ambiente
23
+ * Runs a full environment diagnostic
24
24
  */
25
25
  async diagnose() {
26
- // Usar cache se ainda válido
26
+ // Use the cache if still valid
27
27
  if (this.cache && (Date.now() - this.cacheTime) < this.cacheTTL) {
28
28
  return this.cache;
29
29
  }
@@ -89,7 +89,7 @@ export class EnvironmentDiagnostics {
89
89
  }
90
90
 
91
91
  /**
92
- * Informações do sistema
92
+ * System information
93
93
  */
94
94
  async getSystemInfo() {
95
95
  return {
@@ -110,7 +110,7 @@ export class EnvironmentDiagnostics {
110
110
  }
111
111
 
112
112
  /**
113
- * Detecta Java
113
+ * Detects Java
114
114
  */
115
115
  async detectJava() {
116
116
  const result = {
@@ -122,19 +122,19 @@ export class EnvironmentDiagnostics {
122
122
  };
123
123
 
124
124
  try {
125
- // Tentar java -version
125
+ // Try java -version
126
126
  const { stderr } = await execAsync('java -version 2>&1');
127
127
  const output = stderr || '';
128
128
 
129
129
  result.installed = true;
130
130
 
131
- // Extrair versão (ex: "openjdk version "17.0.1"")
131
+ // Extract the version (e.g.: "openjdk version "17.0.1"")
132
132
  const versionMatch = output.match(/version\s+"([^"]+)"/);
133
133
  if (versionMatch) {
134
134
  result.version = versionMatch[1];
135
135
  }
136
136
 
137
- // Extrair vendor
137
+ // Extract the vendor
138
138
  if (output.includes('OpenJDK')) {
139
139
  result.vendor = 'OpenJDK';
140
140
  } else if (output.includes('Oracle')) {
@@ -147,7 +147,7 @@ export class EnvironmentDiagnostics {
147
147
  result.vendor = 'Eclipse Temurin';
148
148
  }
149
149
 
150
- // Encontrar path
150
+ // Find the path
151
151
  const { stdout: whichOutput } = await execAsync('which java').catch(() => ({ stdout: '' }));
152
152
  result.path = whichOutput.trim() || null;
153
153
 
@@ -159,7 +159,7 @@ export class EnvironmentDiagnostics {
159
159
  }
160
160
 
161
161
  /**
162
- * Detecta Maven
162
+ * Detects Maven
163
163
  */
164
164
  async detectMaven() {
165
165
  const result = {
@@ -174,19 +174,19 @@ export class EnvironmentDiagnostics {
174
174
 
175
175
  result.installed = true;
176
176
 
177
- // Extrair versão (ex: "Apache Maven 3.9.0")
177
+ // Extract the version (e.g.: "Apache Maven 3.9.0")
178
178
  const versionMatch = stdout.match(/Apache Maven (\d+\.\d+\.\d+)/);
179
179
  if (versionMatch) {
180
180
  result.version = versionMatch[1];
181
181
  }
182
182
 
183
- // Extrair Java version usado pelo Maven
183
+ // Extract the Java version used by Maven
184
184
  const javaMatch = stdout.match(/Java version: ([^\n,]+)/);
185
185
  if (javaMatch) {
186
186
  result.javaVersion = javaMatch[1].trim();
187
187
  }
188
188
 
189
- // Encontrar path
189
+ // Find the path
190
190
  const { stdout: whichOutput } = await execAsync('which mvn').catch(() => ({ stdout: '' }));
191
191
  result.path = whichOutput.trim() || null;
192
192
 
@@ -198,7 +198,7 @@ export class EnvironmentDiagnostics {
198
198
  }
199
199
 
200
200
  /**
201
- * Detecta Gradle
201
+ * Detects Gradle
202
202
  */
203
203
  async detectGradle() {
204
204
  const result = {
@@ -213,13 +213,13 @@ export class EnvironmentDiagnostics {
213
213
 
214
214
  result.installed = true;
215
215
 
216
- // Extrair versão (ex: "Gradle 8.0")
216
+ // Extract the version (e.g.: "Gradle 8.0")
217
217
  const versionMatch = stdout.match(/Gradle (\d+\.\d+\.?\d*)/);
218
218
  if (versionMatch) {
219
219
  result.version = versionMatch[1];
220
220
  }
221
221
 
222
- // Encontrar path
222
+ // Find the path
223
223
  const { stdout: whichOutput } = await execAsync('which gradle').catch(() => ({ stdout: '' }));
224
224
  result.path = whichOutput.trim() || null;
225
225
 
@@ -231,7 +231,7 @@ export class EnvironmentDiagnostics {
231
231
  }
232
232
 
233
233
  /**
234
- * Detecta Node.js
234
+ * Detects Node.js
235
235
  */
236
236
  async detectNode() {
237
237
  const result = {
@@ -246,7 +246,7 @@ export class EnvironmentDiagnostics {
246
246
  result.installed = true;
247
247
  result.version = stdout.trim().replace('v', '');
248
248
 
249
- // Encontrar path
249
+ // Find the path
250
250
  const { stdout: whichOutput } = await execAsync('which node').catch(() => ({ stdout: '' }));
251
251
  result.path = whichOutput.trim() || null;
252
252
 
@@ -258,7 +258,7 @@ export class EnvironmentDiagnostics {
258
258
  }
259
259
 
260
260
  /**
261
- * Detecta npm
261
+ * Detects npm
262
262
  */
263
263
  async detectNpm() {
264
264
  const result = {
@@ -278,7 +278,7 @@ export class EnvironmentDiagnostics {
278
278
  const { stdout: prefixOutput } = await execAsync('npm config get prefix').catch(() => ({ stdout: '' }));
279
279
  result.globalPrefix = prefixOutput.trim() || null;
280
280
 
281
- // Encontrar path
281
+ // Find the path
282
282
  const { stdout: whichOutput } = await execAsync('which npm').catch(() => ({ stdout: '' }));
283
283
  result.path = whichOutput.trim() || null;
284
284
 
@@ -290,7 +290,7 @@ export class EnvironmentDiagnostics {
290
290
  }
291
291
 
292
292
  /**
293
- * Detecta Yarn
293
+ * Detects Yarn
294
294
  */
295
295
  async detectYarn() {
296
296
  const result = {
@@ -305,7 +305,7 @@ export class EnvironmentDiagnostics {
305
305
  result.installed = true;
306
306
  result.version = stdout.trim();
307
307
 
308
- // Encontrar path
308
+ // Find the path
309
309
  const { stdout: whichOutput } = await execAsync('which yarn').catch(() => ({ stdout: '' }));
310
310
  result.path = whichOutput.trim() || null;
311
311
 
@@ -317,7 +317,7 @@ export class EnvironmentDiagnostics {
317
317
  }
318
318
 
319
319
  /**
320
- * Detecta Python
320
+ * Detects Python
321
321
  */
322
322
  async detectPython() {
323
323
  const result = {
@@ -328,21 +328,21 @@ export class EnvironmentDiagnostics {
328
328
  };
329
329
 
330
330
  try {
331
- // Tentar python3 primeiro
331
+ // Try python3 first
332
332
  const { stdout } = await execAsync('python3 --version 2>&1').catch(() =>
333
333
  execAsync('python --version 2>&1')
334
334
  );
335
335
 
336
336
  result.installed = true;
337
337
 
338
- // Extrair versão (ex: "Python 3.11.0")
338
+ // Extract the version (e.g.: "Python 3.11.0")
339
339
  const versionMatch = stdout.match(/Python (\d+\.\d+\.?\d*)/);
340
340
  if (versionMatch) {
341
341
  result.version = versionMatch[1];
342
342
  result.python3 = result.version.startsWith('3');
343
343
  }
344
344
 
345
- // Encontrar path
345
+ // Find the path
346
346
  const { stdout: whichOutput } = await execAsync('which python3 || which python').catch(() => ({ stdout: '' }));
347
347
  result.path = whichOutput.trim() || null;
348
348
 
@@ -354,7 +354,7 @@ export class EnvironmentDiagnostics {
354
354
  }
355
355
 
356
356
  /**
357
- * Detecta pip
357
+ * Detects pip
358
358
  */
359
359
  async detectPip() {
360
360
  const result = {
@@ -370,13 +370,13 @@ export class EnvironmentDiagnostics {
370
370
 
371
371
  result.installed = true;
372
372
 
373
- // Extrair versão (ex: "pip 23.0.1")
373
+ // Extract the version (e.g.: "pip 23.0.1")
374
374
  const versionMatch = stdout.match(/pip (\d+\.\d+\.?\d*)/);
375
375
  if (versionMatch) {
376
376
  result.version = versionMatch[1];
377
377
  }
378
378
 
379
- // Encontrar path
379
+ // Find the path
380
380
  const { stdout: whichOutput } = await execAsync('which pip3 || which pip').catch(() => ({ stdout: '' }));
381
381
  result.path = whichOutput.trim() || null;
382
382
 
@@ -388,7 +388,7 @@ export class EnvironmentDiagnostics {
388
388
  }
389
389
 
390
390
  /**
391
- * Detecta Go
391
+ * Detects Go
392
392
  */
393
393
  async detectGo() {
394
394
  const result = {
@@ -404,13 +404,13 @@ export class EnvironmentDiagnostics {
404
404
 
405
405
  result.installed = true;
406
406
 
407
- // Extrair versão (ex: "go version go1.21.0 darwin/amd64")
407
+ // Extract the version (e.g.: "go version go1.21.0 darwin/amd64")
408
408
  const versionMatch = stdout.match(/go(\d+\.\d+\.?\d*)/);
409
409
  if (versionMatch) {
410
410
  result.version = versionMatch[1];
411
411
  }
412
412
 
413
- // Encontrar path
413
+ // Find the path
414
414
  const { stdout: whichOutput } = await execAsync('which go').catch(() => ({ stdout: '' }));
415
415
  result.path = whichOutput.trim() || null;
416
416
 
@@ -422,7 +422,7 @@ export class EnvironmentDiagnostics {
422
422
  }
423
423
 
424
424
  /**
425
- * Detecta .NET
425
+ * Detects .NET
426
426
  */
427
427
  async detectDotnet() {
428
428
  const result = {
@@ -447,7 +447,7 @@ export class EnvironmentDiagnostics {
447
447
  const { stdout: runtimeOutput } = await execAsync('dotnet --list-runtimes 2>&1').catch(() => ({ stdout: '' }));
448
448
  result.runtimes = runtimeOutput.trim().split('\n').filter(Boolean).slice(0, 5); // Limitar
449
449
 
450
- // Encontrar path
450
+ // Find the path
451
451
  const { stdout: whichOutput } = await execAsync('which dotnet').catch(() => ({ stdout: '' }));
452
452
  result.path = whichOutput.trim() || null;
453
453
 
@@ -459,7 +459,7 @@ export class EnvironmentDiagnostics {
459
459
  }
460
460
 
461
461
  /**
462
- * Detecta Git
462
+ * Detects Git
463
463
  */
464
464
  async detectGit() {
465
465
  const result = {
@@ -475,7 +475,7 @@ export class EnvironmentDiagnostics {
475
475
 
476
476
  result.installed = true;
477
477
 
478
- // Extrair versão (ex: "git version 2.39.0")
478
+ // Extract the version (e.g.: "git version 2.39.0")
479
479
  const versionMatch = stdout.match(/git version (\d+\.\d+\.?\d*)/);
480
480
  if (versionMatch) {
481
481
  result.version = versionMatch[1];
@@ -488,7 +488,7 @@ export class EnvironmentDiagnostics {
488
488
  const { stdout: emailOutput } = await execAsync('git config --global user.email').catch(() => ({ stdout: '' }));
489
489
  result.email = emailOutput.trim() || null;
490
490
 
491
- // Encontrar path
491
+ // Find the path
492
492
  const { stdout: whichOutput } = await execAsync('which git').catch(() => ({ stdout: '' }));
493
493
  result.path = whichOutput.trim() || null;
494
494
 
@@ -500,7 +500,7 @@ export class EnvironmentDiagnostics {
500
500
  }
501
501
 
502
502
  /**
503
- * Detecta Docker
503
+ * Detects Docker
504
504
  */
505
505
  async detectDocker() {
506
506
  const result = {
@@ -515,13 +515,13 @@ export class EnvironmentDiagnostics {
515
515
 
516
516
  result.installed = true;
517
517
 
518
- // Extrair versão (ex: "Docker version 24.0.0, build xxx")
518
+ // Extract the version (e.g.: "Docker version 24.0.0, build xxx")
519
519
  const versionMatch = stdout.match(/Docker version (\d+\.\d+\.?\d*)/);
520
520
  if (versionMatch) {
521
521
  result.version = versionMatch[1];
522
522
  }
523
523
 
524
- // Verificar se está rodando
524
+ // Check whether it is running
525
525
  try {
526
526
  await execAsync('docker info 2>&1');
527
527
  result.running = true;
@@ -529,7 +529,7 @@ export class EnvironmentDiagnostics {
529
529
  result.running = false;
530
530
  }
531
531
 
532
- // Encontrar path
532
+ // Find the path
533
533
  const { stdout: whichOutput } = await execAsync('which docker').catch(() => ({ stdout: '' }));
534
534
  result.path = whichOutput.trim() || null;
535
535
 
@@ -541,7 +541,7 @@ export class EnvironmentDiagnostics {
541
541
  }
542
542
 
543
543
  /**
544
- * Gera sumário das capacidades
544
+ * Generates a capabilities summary
545
545
  */
546
546
  generateSummary(tools) {
547
547
  const capabilities = [];
@@ -579,47 +579,47 @@ export class EnvironmentDiagnostics {
579
579
  }
580
580
 
581
581
  /**
582
- * Detecta problemas de configuração
582
+ * Detects configuration problems
583
583
  */
584
584
  detectIssues(tools) {
585
585
  const issues = [];
586
586
 
587
- // Java sem JAVA_HOME
587
+ // Java without JAVA_HOME
588
588
  if (tools.java?.installed && !process.env.JAVA_HOME) {
589
589
  issues.push({
590
590
  severity: 'warning',
591
591
  tool: 'java',
592
- message: 'JAVA_HOME não está configurado',
592
+ message: 'JAVA_HOME is not configured',
593
593
  fix: 'export JAVA_HOME=$(/usr/libexec/java_home)'
594
594
  });
595
595
  }
596
596
 
597
- // Java instalado mas Maven não
597
+ // Java installed but Maven not
598
598
  if (tools.java?.installed && !tools.maven?.installed) {
599
599
  issues.push({
600
600
  severity: 'info',
601
601
  tool: 'maven',
602
- message: 'Maven não está instalado (necessário para projetos Maven)',
602
+ message: 'Maven is not installed (required for Maven projects)',
603
603
  fix: 'brew install maven'
604
604
  });
605
605
  }
606
606
 
607
- // Node sem npm
607
+ // Node without npm
608
608
  if (tools.node?.installed && !tools.npm?.installed) {
609
609
  issues.push({
610
610
  severity: 'error',
611
611
  tool: 'npm',
612
- message: 'Node está instalado mas npm não foi encontrado',
613
- fix: 'Reinstale o Node.js'
612
+ message: 'Node is installed but npm was not found',
613
+ fix: 'Reinstall Node.js'
614
614
  });
615
615
  }
616
616
 
617
- // Python 2 ao invés de 3
617
+ // Python 2 instead of 3
618
618
  if (tools.python?.installed && !tools.python?.python3) {
619
619
  issues.push({
620
620
  severity: 'warning',
621
621
  tool: 'python',
622
- message: 'Python 2 detectado. Recomendado usar Python 3',
622
+ message: 'Python 2 detected. Using Python 3 is recommended',
623
623
  fix: 'brew install python3'
624
624
  });
625
625
  }
@@ -628,7 +628,7 @@ export class EnvironmentDiagnostics {
628
628
  }
629
629
 
630
630
  /**
631
- * Diagnóstico rápido apenas para uma linguagem
631
+ * Quick diagnostic for a single language only
632
632
  */
633
633
  async diagnoseFor(language) {
634
634
  switch (language) {