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
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepdebug-local-agent",
3
- "version": "1.0.23",
3
+ "version": "1.2.0",
4
4
  "description": "DeepDebug Local Agent - AI-powered code debugging assistant",
5
5
  "private": false,
6
6
  "type": "module",
@@ -41,9 +41,14 @@
41
41
  "js-yaml": "^4.1.0",
42
42
  "pg": "^8.20.0",
43
43
  "pidusage": "^3.0.2",
44
+ "pino": "^10.3.1",
45
+ "prom-client": "^15.1.3",
44
46
  "properties-reader": "^2.3.0",
45
47
  "ssh2": "^1.16.0",
46
48
  "strip-ansi": "^7.1.0",
49
+ "tree-sitter": "^0.22.4",
50
+ "tree-sitter-swift": "^0.7.1",
51
+ "typescript": "^5.9.3",
47
52
  "unidiff": "^1.0.2",
48
53
  "ws": "^8.18.0"
49
54
  },
@@ -75,10 +80,10 @@
75
80
  "license": "Proprietary",
76
81
  "repository": {
77
82
  "type": "git",
78
- "url": "https://github.com/williambella/deepdebug-local-agent.git"
83
+ "url": "git+https://github.com/VISIONHAMMER/deepdebug-local-agent.git"
79
84
  },
80
- "homepage": "https://deepdebug.ai",
85
+ "homepage": "https://visionhammer.io",
81
86
  "bugs": {
82
- "url": "https://github.com/williambella/deepdebug-local-agent/issues"
87
+ "url": "https://github.com/VISIONHAMMER/deepdebug-local-agent/issues"
83
88
  }
84
89
  }
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * next-version.mjs: semantic version for the automatic npm release.
4
+ *
5
+ * major (X.0.0) a breaking change: "type!:" subject or a "BREAKING CHANGE:" footer
6
+ * minor (0.X.0) new functionality: a "feat:" commit
7
+ * patch (0.0.X) everything else (fix, perf, refactor, docs, chore, ci, ...)
8
+ *
9
+ * Conventional Commits. The highest level among the commits since the last release
10
+ * tag wins. Merge commits and the CI auto-bump commits ("[skip ci]") are ignored.
11
+ *
12
+ * CLI: git log -z --no-merges --format=%B <range> | node scripts/next-version.mjs <base-version>
13
+ * prints the next version; stderr says which commit decided the bump.
14
+ */
15
+ import { fileURLToPath } from 'node:url'
16
+
17
+ const SUBJECT_TYPE = /^([a-z]+)(\([^)]*\))?(!)?:\s/i
18
+ const BREAKING_FOOTER = /^BREAKING[ -]CHANGE:\s/m
19
+
20
+ /** @param {string} message full commit message (subject + body) */
21
+ export function classify(message) {
22
+ const text = String(message || '').trim()
23
+ if (!text) return null
24
+ const subject = text.split('\n', 1)[0]
25
+ if (/\[skip ci\]/i.test(subject) || /^Merge /.test(subject)) return null
26
+ const m = SUBJECT_TYPE.exec(subject)
27
+ if (BREAKING_FOOTER.test(text) || (m && m[3])) return 'major'
28
+ if (m && m[1].toLowerCase() === 'feat') return 'minor'
29
+ return 'patch'
30
+ }
31
+
32
+ const RANK = { patch: 0, minor: 1, major: 2 }
33
+
34
+ /** @param {string[]} messages commit messages since the last release */
35
+ export function bumpFor(messages) {
36
+ let level = 'patch'
37
+ for (const msg of messages) {
38
+ const c = classify(msg)
39
+ if (c && RANK[c] > RANK[level]) level = c
40
+ }
41
+ return level
42
+ }
43
+
44
+ /** @param {string} base X.Y.Z @param {'major'|'minor'|'patch'} bump */
45
+ export function nextVersion(base, bump) {
46
+ const parts = String(base).split('.').map(Number)
47
+ if (parts.length !== 3 || parts.some((n) => !Number.isInteger(n) || n < 0)) {
48
+ throw new Error(`base version '${base}' is not X.Y.Z`)
49
+ }
50
+ const [a, b, c] = parts
51
+ if (bump === 'major') return `${a + 1}.0.0`
52
+ if (bump === 'minor') return `${a}.${b + 1}.0`
53
+ return `${a}.${b}.${c + 1}`
54
+ }
55
+
56
+ async function main() {
57
+ const base = process.argv[2]
58
+ if (!base) {
59
+ console.error('usage: next-version.mjs <base-version> < commit messages separated by NUL')
60
+ process.exit(2)
61
+ }
62
+ const chunks = []
63
+ for await (const chunk of process.stdin) chunks.push(chunk)
64
+ const messages = Buffer.concat(chunks).toString('utf8').split('\0')
65
+ const bump = bumpFor(messages)
66
+ console.error(`[next-version] ${messages.filter((m) => classify(m)).length} releasable commit(s) since the last release, bump=${bump}`)
67
+ console.log(nextVersion(base, bump))
68
+ }
69
+
70
+ if (process.argv[1] === fileURLToPath(import.meta.url)) {
71
+ main().catch((e) => {
72
+ console.error(e.message)
73
+ process.exit(1)
74
+ })
75
+ }
@@ -0,0 +1,227 @@
1
+ #!/usr/bin/env bash
2
+ # Upgrade an installed DeepDebug client observability bundle so that:
3
+ # 1. Alloy also pushes the Docker log stream to VisionHammer
4
+ # (OTLP/HTTP JSON -> <gateway>/api/v1/webhooks/logs/otlp, Bearer <trigger secret>),
5
+ # which feeds Live logs and the services list;
6
+ # 2. Alertmanager sends the trigger secret as a Bearer header instead of ?s= in the URL.
7
+ #
8
+ # Safe to re-run (idempotent). Backs up both files, validates the new configs with the
9
+ # same images the stack runs, restarts only alloy + alertmanager, and rolls back on failure.
10
+ #
11
+ # Usage (on the server, as a user that can run docker):
12
+ # sudo bash upgrade-observability-push.sh # apply
13
+ # sudo bash upgrade-observability-push.sh --dry-run # show what would change
14
+ # Optional env:
15
+ # INSTALL_DIR=/path/to/deepdebug-observability (auto-detected otherwise)
16
+ # GATEWAY_URL=https://api-qa.visionhammer.io (read from alertmanager.yml otherwise)
17
+ set -euo pipefail
18
+
19
+ DRY_RUN=0
20
+ [[ "${1:-}" == "--dry-run" ]] && DRY_RUN=1
21
+
22
+ log() { printf '\033[1;34m[upgrade]\033[0m %s\n' "$*"; }
23
+ warn() { printf '\033[1;33m[upgrade]\033[0m %s\n' "$*" >&2; }
24
+ die() { printf '\033[1;31m[upgrade]\033[0m %s\n' "$*" >&2; exit 1; }
25
+ mask() { local s="$1"; printf '%s...%s' "${s:0:8}" "${s: -4}"; }
26
+
27
+ command -v docker >/dev/null || die "docker not found"
28
+ docker compose version >/dev/null 2>&1 || die "docker compose plugin not found"
29
+ command -v curl >/dev/null || die "curl not found"
30
+
31
+ # ---------------------------------------------------------------- locate the stack
32
+ find_install_dir() {
33
+ if [[ -n "${INSTALL_DIR:-}" ]]; then echo "$INSTALL_DIR"; return; fi
34
+ # 1) the running compose project
35
+ local cfg
36
+ cfg=$(docker compose ls --all --format json 2>/dev/null \
37
+ | tr '{' '\n' | grep '"Name":"deepdebug-observability"' \
38
+ | sed -n 's/.*"ConfigFiles":"\([^",]*\).*/\1/p' | head -1 || true)
39
+ if [[ -n "$cfg" && -f "$cfg" ]]; then dirname "$cfg"; return; fi
40
+ # 2) default provision paths
41
+ local d
42
+ for d in "$HOME/.deepdebug/observability/deepdebug-observability" \
43
+ /root/.deepdebug/observability/deepdebug-observability \
44
+ /home/*/.deepdebug/observability/deepdebug-observability; do
45
+ [[ -f "$d/docker-compose.yml" ]] && { echo "$d"; return; }
46
+ done
47
+ }
48
+
49
+ DIR=$(find_install_dir || true)
50
+ [[ -n "$DIR" && -f "$DIR/docker-compose.yml" ]] \
51
+ || die "observability bundle not found; set INSTALL_DIR=/path/to/deepdebug-observability"
52
+ ALLOY="$DIR/alloy/config.alloy"
53
+ AM="$DIR/alertmanager/alertmanager.yml"
54
+ [[ -f "$ALLOY" ]] || die "missing $ALLOY"
55
+ [[ -f "$AM" ]] || die "missing $AM"
56
+ log "bundle: $DIR"
57
+
58
+ # ---------------------------------------------------------------- secret + gateway
59
+ SECRET=$(grep -oE 'vht_[A-Za-z0-9._-]+' "$AM" | head -1 || true)
60
+ [[ -n "$SECRET" ]] || die "no vht_ trigger secret found in $AM"
61
+ if [[ -z "${GATEWAY_URL:-}" ]]; then
62
+ GATEWAY_URL=$(grep -oE "https?://[^/'\" ]+" "$AM" | head -1 || true)
63
+ fi
64
+ GATEWAY_URL="${GATEWAY_URL%/}"
65
+ [[ "$GATEWAY_URL" =~ ^https?://[A-Za-z0-9.:-]+$ ]] || die "could not determine GATEWAY_URL (set it explicitly)"
66
+ log "gateway: $GATEWAY_URL secret: $(mask "$SECRET")"
67
+ case "$GATEWAY_URL" in
68
+ https://api-qa.visionhammer.io|https://api.visionhammer.io) ;;
69
+ *) warn "gateway is not api-qa/api.visionhammer.io; the push endpoint only exists on the current gateway." ;;
70
+ esac
71
+
72
+ # ---------------------------------------------------------------- 1. probe the endpoint
73
+ # An empty OTLP batch authenticates and registers the SELF_HOSTED log source without
74
+ # writing any log line.
75
+ code=$(curl -sS -o /tmp/vh-otlp-probe.$$ -w '%{http_code}' -m 20 \
76
+ -X POST "$GATEWAY_URL/api/v1/webhooks/logs/otlp" \
77
+ -H "Authorization: Bearer $SECRET" -H 'Content-Type: application/json' \
78
+ --data '{"resourceLogs":[]}' || echo 000)
79
+ case "$code" in
80
+ 200) log "push endpoint OK (200)" ;;
81
+ 401) die "push endpoint rejected the secret (401): the trigger source is disabled or the secret changed" ;;
82
+ 404) die "push endpoint not deployed yet on $GATEWAY_URL (404); retry after the gateway deploy" ;;
83
+ *) die "push endpoint probe failed (HTTP $code): $(head -c 300 /tmp/vh-otlp-probe.$$ 2>/dev/null)" ;;
84
+ esac
85
+ rm -f /tmp/vh-otlp-probe.$$
86
+
87
+ # ---------------------------------------------------------------- 2. build new configs
88
+ TMP=$(mktemp -d)
89
+ trap 'rm -rf "$TMP"' EXIT
90
+ cp "$ALLOY" "$TMP/config.alloy"
91
+ cp "$AM" "$TMP/alertmanager.yml"
92
+
93
+ alloy_changed=0
94
+ if grep -q 'otelcol.exporter.otlphttp "visionhammer"' "$TMP/config.alloy"; then
95
+ log "alloy: push already configured"
96
+ else
97
+ # loki.process "default" must also forward to the OTLP receiver.
98
+ n=$(grep -c 'forward_to = \[loki.write.default.receiver\]' "$TMP/config.alloy" || true)
99
+ [[ "$n" == "1" ]] || die "alloy config does not look like the stock bundle (expected one 'forward_to = [loki.write.default.receiver]', found $n); edit $ALLOY by hand"
100
+ sed -i 's|forward_to = \[loki.write.default.receiver\]|forward_to = [loki.write.default.receiver, otelcol.receiver.loki.visionhammer.receiver]|' "$TMP/config.alloy"
101
+ cat >> "$TMP/config.alloy" <<EOF
102
+
103
+ // ---- push to VisionHammer: Loki entries -> OTLP logs (labels become attributes) ----
104
+ otelcol.receiver.loki "visionhammer" {
105
+ output {
106
+ logs = [otelcol.processor.batch.visionhammer.input]
107
+ }
108
+ }
109
+
110
+ otelcol.processor.batch "visionhammer" {
111
+ send_batch_size = 500
112
+ send_batch_max_size = 2000
113
+ timeout = "5s"
114
+ output {
115
+ logs = [otelcol.exporter.otlphttp.visionhammer.input]
116
+ }
117
+ }
118
+
119
+ otelcol.exporter.otlphttp "visionhammer" {
120
+ client {
121
+ endpoint = "$GATEWAY_URL"
122
+ compression = "gzip"
123
+ headers = { "Authorization" = "Bearer $SECRET" }
124
+ }
125
+ encoding = "json"
126
+ logs_endpoint = "$GATEWAY_URL/api/v1/webhooks/logs/otlp"
127
+ }
128
+ EOF
129
+ alloy_changed=1
130
+ log "alloy: push blocks added"
131
+ fi
132
+
133
+ am_changed=0
134
+ if grep -q 'webhooks/alertmanager?s=' "$TMP/alertmanager.yml"; then
135
+ # Drop ?s=... from the URL and add http_config.authorization at the same indent
136
+ # as the url key of that list item.
137
+ # Each receiver keeps ITS OWN secret (a bundle may post to QA and PROD gateways,
138
+ # each registered with a different vht_ secret).
139
+ awk '
140
+ /webhooks\/alertmanager\?s=/ {
141
+ match($0, /vht_[A-Za-z0-9._-]+/)
142
+ secret = substr($0, RSTART, RLENGTH)
143
+ line = $0
144
+ sub(/\?s=[^'"'"'" ]*/, "", line)
145
+ print line
146
+ match($0, /^[ ]*(- )?/)
147
+ ind = RLENGTH
148
+ pad = sprintf("%" ind "s", "")
149
+ print pad "http_config:"
150
+ print pad " authorization:"
151
+ print pad " type: Bearer"
152
+ print pad " credentials: '"'"'" secret "'"'"'"
153
+ next
154
+ }
155
+ { print }
156
+ ' "$TMP/alertmanager.yml" > "$TMP/alertmanager.yml.new"
157
+ mv "$TMP/alertmanager.yml.new" "$TMP/alertmanager.yml"
158
+ am_changed=1
159
+ log "alertmanager: secret moved from ?s= to Authorization header"
160
+ else
161
+ log "alertmanager: already uses header auth (or a custom config); left unchanged"
162
+ fi
163
+
164
+ if [[ $alloy_changed == 0 && $am_changed == 0 ]]; then
165
+ log "nothing to do"; exit 0
166
+ fi
167
+
168
+ # Mask every vht_ secret in printed output, not only the first one.
169
+ mask_all() { sed -E 's/(vht_[A-Za-z0-9]{4})[A-Za-z0-9._-]*([A-Za-z0-9._-]{4})/\1...\2/g'; }
170
+ show_diff() { diff -u "$1" "$2" | mask_all || true; }
171
+ [[ $alloy_changed == 1 ]] && show_diff "$ALLOY" "$TMP/config.alloy"
172
+ [[ $am_changed == 1 ]] && show_diff "$AM" "$TMP/alertmanager.yml"
173
+
174
+ if [[ $DRY_RUN == 1 ]]; then log "dry run: nothing written"; exit 0; fi
175
+
176
+ # ---------------------------------------------------------------- 3. validate with the stack's images
177
+ image_of() { grep -E "^\s*image:\s*.*$1" "$DIR/docker-compose.yml" | head -1 | awk '{print $2}'; }
178
+ ALLOY_IMG=$(image_of 'grafana/alloy'); ALLOY_IMG=${ALLOY_IMG:-grafana/alloy:v1.3.1}
179
+ AM_IMG=$(image_of 'prom/alertmanager'); AM_IMG=${AM_IMG:-prom/alertmanager:v0.27.0}
180
+
181
+ if [[ $alloy_changed == 1 ]]; then
182
+ docker run --rm -v "$TMP/config.alloy:/c/config.alloy:ro" "$ALLOY_IMG" fmt /c/config.alloy >/dev/null \
183
+ || die "new alloy config failed validation; nothing changed"
184
+ log "alloy config valid ($ALLOY_IMG)"
185
+ fi
186
+ if [[ $am_changed == 1 ]]; then
187
+ docker run --rm --entrypoint amtool -v "$TMP/alertmanager.yml:/c/am.yml:ro" "$AM_IMG" check-config /c/am.yml >/dev/null \
188
+ || die "new alertmanager config failed validation; nothing changed"
189
+ log "alertmanager config valid ($AM_IMG)"
190
+ fi
191
+
192
+ # ---------------------------------------------------------------- 4. apply + restart
193
+ STAMP=$(date +%Y%m%d-%H%M%S)
194
+ cp -p "$ALLOY" "$ALLOY.bak-$STAMP"
195
+ cp -p "$AM" "$AM.bak-$STAMP"
196
+ log "backups: *.bak-$STAMP"
197
+ # Overwrite in place (keeps owner/permissions and the bind-mounted inode semantics on restart).
198
+ cat "$TMP/config.alloy" > "$ALLOY"
199
+ cat "$TMP/alertmanager.yml" > "$AM"
200
+
201
+ rollback() {
202
+ warn "rolling back to *.bak-$STAMP"
203
+ cat "$ALLOY.bak-$STAMP" > "$ALLOY"
204
+ cat "$AM.bak-$STAMP" > "$AM"
205
+ (cd "$DIR" && docker compose restart alloy alertmanager >/dev/null 2>&1) || true
206
+ die "$1"
207
+ }
208
+
209
+ (cd "$DIR" && docker compose restart alloy alertmanager) || rollback "restart failed"
210
+
211
+ # ---------------------------------------------------------------- 5. verify
212
+ log "waiting 30s for alloy to ship its first batch..."
213
+ sleep 30
214
+ running=$(cd "$DIR" && docker compose ps --status running --services 2>/dev/null || true)
215
+ grep -qx alloy <<<"$running" || rollback "alloy is not running after the restart"
216
+ grep -qx alertmanager <<<"$running" || rollback "alertmanager is not running after the restart"
217
+
218
+ errs=$(cd "$DIR" && docker compose logs --since 40s alloy 2>&1 \
219
+ | grep -iE 'level=error|Exporting failed|permanent error' | grep -i 'visionhammer\|otlphttp' || true)
220
+ if [[ -n "$errs" ]]; then
221
+ warn "alloy reported export errors:"
222
+ mask_all <<<"$errs" | tail -5 >&2
223
+ rollback "alloy cannot push to $GATEWAY_URL"
224
+ fi
225
+
226
+ log "done. Logs should appear in VisionHammer > Live logs within ~1-2 minutes."
227
+ log "to undo: cp $ALLOY.bak-$STAMP $ALLOY; cp $AM.bak-$STAMP $AM; (cd $DIR && docker compose restart alloy alertmanager)"
package/src/ai-engine.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // ============================================
2
2
  // 🧠 AI VIBE CODING ENGINE
3
- // Ficheiro: ai-engine.js
3
+ // File: ai-engine.js
4
4
  // Coloca em: ~/deepdebug-local-agent/src/ai-engine.js
5
5
  // ============================================
6
6
 
@@ -15,15 +15,15 @@ const execAsync = promisify(exec);
15
15
  /**
16
16
  * AIVibeCodingEngine
17
17
  *
18
- * Sistema universal de auto-healing que usa AI para resolver
19
- * QUALQUER erro automaticamente - não apenas startup.
18
+ * Universal auto-healing system that uses AI to resolve
19
+ * ANY error automatically - not just startup.
20
20
  *
21
21
  * Features:
22
- * - Monitorização de erros em tempo real
23
- * - Classificação automática de erros
22
+ * - Real-time error monitoring
23
+ * - Automatic error classification
24
24
  * - Quick fixes locais (sem chamar AI)
25
- * - Escala para AI quando quick fixes falham
26
- * - Histórico de fixes para reutilização
25
+ * - Escalates to AI when quick fixes fail
26
+ * - Fix history for reuse
27
27
  */
28
28
  export class AIVibeCodingEngine extends EventEmitter {
29
29
  constructor(processManager, getWorkspaceRoot) {
@@ -34,12 +34,12 @@ export class AIVibeCodingEngine extends EventEmitter {
34
34
  this.maxRetries = 3;
35
35
  this.isActive = true;
36
36
 
37
- // Histórico
37
+ // History
38
38
  this.errorHistory = [];
39
39
  this.fixHistory = [];
40
40
  this.pendingFixes = [];
41
41
 
42
- // Estado
42
+ // State
43
43
  this.currentSession = null;
44
44
  this.lastSuccessfulConfig = null;
45
45
 
@@ -54,14 +54,14 @@ export class AIVibeCodingEngine extends EventEmitter {
54
54
  // ============================================
55
55
 
56
56
  setupErrorMonitoring() {
57
- // Monitorar logs do ProcessManager
57
+ // Monitor ProcessManager logs
58
58
  this.processManager.on('log', async ({ serviceId, message, type }) => {
59
59
  if (this.isActive && message && this.isError(message)) {
60
60
  await this.handleRuntimeError(serviceId, message, type);
61
61
  }
62
62
  });
63
63
 
64
- // Monitorar crashes
64
+ // Monitor crashes
65
65
  this.processManager.on('stopped', async ({ serviceId, code, signal }) => {
66
66
  if (this.isActive && code !== 0 && code !== null) {
67
67
  console.log(`🧠 [AI-Engine] Process ${serviceId} crashed (code: ${code})`);
@@ -69,7 +69,7 @@ export class AIVibeCodingEngine extends EventEmitter {
69
69
  }
70
70
  });
71
71
 
72
- // Monitorar erros de processo
72
+ // Monitor process errors
73
73
  this.processManager.on('error', async ({ serviceId, error }) => {
74
74
  if (this.isActive) {
75
75
  console.log(`🧠 [AI-Engine] Process error: ${error}`);
@@ -212,7 +212,7 @@ export class AIVibeCodingEngine extends EventEmitter {
212
212
  async handleRuntimeError(serviceId, errorMessage, type) {
213
213
  const classification = this.classifyError(errorMessage);
214
214
 
215
- // Debounce: evitar duplicatas em 5 segundos
215
+ // Debounce: avoid duplicates within 5 seconds
216
216
  const recentSimilar = this.errorHistory.find(e =>
217
217
  e.classification.type === classification.type &&
218
218
  Date.now() - e.timestamp < 5000
@@ -235,10 +235,10 @@ export class AIVibeCodingEngine extends EventEmitter {
235
235
  console.log(`🧠 [AI-Engine] Error detected: ${classification.type} (${classification.severity})`);
236
236
  console.log(` Message: ${errorMessage.substring(0, 150)}...`);
237
237
 
238
- // Emitir evento
238
+ // Emit the event
239
239
  this.emit('error_detected', errorEntry);
240
240
 
241
- // Se auto-fixável, preparar fix
241
+ // If auto-fixable, prepare a fix
242
242
  if (classification.autoFixable) {
243
243
  const quickFix = this.getQuickFix(classification.type, errorMessage);
244
244
  if (quickFix) {
@@ -357,14 +357,14 @@ export class AIVibeCodingEngine extends EventEmitter {
357
357
  attempts++;
358
358
  console.log(`\n🔄 [AI-Engine] Attempt ${attempts}/${this.maxRetries}`);
359
359
 
360
- // Aplicar fixes pendentes
360
+ // Apply pending fixes
361
361
  if (attempts > 1 && this.pendingFixes.length > 0) {
362
362
  const fix = this.pendingFixes.shift();
363
363
  console.log(`💡 [AI-Engine] Applying fix: ${fix.fix.description}`);
364
364
  currentConfig = await this.applyFix(currentConfig, fix.fix);
365
365
  }
366
366
 
367
- // Recompilar se necessário
367
+ // Recompile if needed
368
368
  if (currentConfig.recompile) {
369
369
  console.log('🔨 [AI-Engine] Recompiling...');
370
370
  const compileResult = await this.recompile();
@@ -379,7 +379,7 @@ export class AIVibeCodingEngine extends EventEmitter {
379
379
  delete currentConfig.recompile;
380
380
  }
381
381
 
382
- // Tentar iniciar
382
+ // Try to start
383
383
  const result = await this.attemptStart(currentConfig);
384
384
 
385
385
  this.currentSession.attempts.push({
@@ -419,14 +419,14 @@ export class AIVibeCodingEngine extends EventEmitter {
419
419
  lastError = result.error;
420
420
  console.log(`❌ [AI-Engine] Attempt ${attempts} failed: ${lastError?.substring(0, 100)}...`);
421
421
 
422
- // Obter fix
422
+ // Get a fix
423
423
  const fix = await this.getFix(result.error, result.logs, currentConfig);
424
424
 
425
425
  if (fix) {
426
426
  console.log(`💡 [AI-Engine] Fix: ${fix.description}`);
427
427
  currentConfig = await this.applyFix(currentConfig, fix);
428
428
  } else {
429
- // Tentar AI
429
+ // Try AI
430
430
  console.log(`🤖 [AI-Engine] Requesting AI analysis...`);
431
431
  const aiAnalysis = await this.analyzeWithAI('startup', lastError, currentConfig);
432
432
  if (aiAnalysis?.newConfig) {
@@ -536,7 +536,7 @@ export class AIVibeCodingEngine extends EventEmitter {
536
536
  return quickFix;
537
537
  }
538
538
 
539
- // Verificar histórico
539
+ // Check the history
540
540
  const similarFix = this.findSimilarFix(classification.type);
541
541
  if (similarFix) {
542
542
  console.log(`📚 [AI-Engine] Found similar fix in history`);
@@ -707,7 +707,7 @@ export class AIVibeCodingEngine extends EventEmitter {
707
707
  }
708
708
 
709
709
  try {
710
- // Importar detectProject dinamicamente para evitar dependência circular
710
+ // Import detectProject dynamically to avoid a circular dependency
711
711
  const { detectProject } = await import('./detectors.js');
712
712
  const meta = await detectProject(workspaceRoot);
713
713
 
@@ -0,0 +1,3 @@
1
+ [
2
+ { "keyId": "vh-2026-09", "publicKey": "MCowBQYDK2VwAyEAp1QFJcXW1Xgz+B0Qppsgqc87rZmwrggKYC3dh4n7aBg=" }
3
+ ]
@@ -0,0 +1,50 @@
1
+ /**
2
+ * build-tool-detector.js — PASSO 0 of the fix validation ladder.
3
+ *
4
+ * Thin facade over the battle-tested detectProject() (src/detectors.js) that
5
+ * normalizes its output into the {stack, tier} shape the auto-fix engine
6
+ * consumes. One source of truth: marker priority, multi-language and the
7
+ * "Swift wins" product rule all live in detectProject — this file only maps.
8
+ *
9
+ * Tiers (validation ladder):
10
+ * A = C1 structural + C2 syntax + C3 build (maven/gradle/node)
11
+ * B = C1 + C2, build unsupported on this host (xcode/swift — needs macOS)
12
+ * C = C1 only (anything unrecognized)
13
+ */
14
+ import { detectProject } from '../detectors.js';
15
+
16
+ const STACK_BY_BUILD_TOOL = {
17
+ maven: 'maven',
18
+ gradle: 'gradle',
19
+ npm: 'node',
20
+ yarn: 'node',
21
+ pnpm: 'node',
22
+ xcodebuild: 'xcode',
23
+ swiftpm: 'xcode',
24
+ cocoapods: 'xcode',
25
+ go: 'go',
26
+ pip: 'python',
27
+ poetry: 'python',
28
+ bundler: 'ruby',
29
+ rake: 'ruby',
30
+ };
31
+
32
+ const TIER_BY_STACK = {
33
+ maven: 'A',
34
+ gradle: 'A',
35
+ node: 'A',
36
+ xcode: 'B',
37
+ };
38
+
39
+ export async function detectBuildTool(workspacePath) {
40
+ const meta = await detectProject(workspacePath);
41
+ const buildTool = meta?.buildTool || 'unknown';
42
+ const stack = STACK_BY_BUILD_TOOL[buildTool] || 'unknown';
43
+ return {
44
+ stack,
45
+ tier: TIER_BY_STACK[stack] || 'C',
46
+ language: meta?.language || 'unknown',
47
+ buildTool,
48
+ marker: meta?.marker || null,
49
+ };
50
+ }