deepdebug-local-agent 1.0.22 → 1.1.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.
- package/.claude/context.md +76 -0
- package/.claude/skills/README.md +25 -0
- package/.claude/skills/tdd-debugging/SKILL.md +53 -0
- package/.claude/skills/tdd-debugging/references/anti-patterns.md +43 -0
- package/.claude/skills/tdd-debugging/references/command-reference.md +46 -0
- package/.claude/standards/README.md +51 -0
- package/.claude/standards/clean-code.md +75 -0
- package/.claude/standards/code-review-checklist.md +61 -0
- package/.claude/standards/layered-architecture.md +163 -0
- package/.claude/standards/node.md +45 -0
- package/.claude/standards/security-owasp.md +114 -0
- package/.claude/standards/simplicity.md +42 -0
- package/.claude/standards/solid.md +64 -0
- package/.claude/standards/testing.md +59 -0
- package/.cursor/rules/standards.mdc +17 -0
- package/.github/workflows/build-byoc-image.yml +315 -0
- package/.github/workflows/deploy-gcp.yml +102 -0
- package/.github/workflows/npm-publish.yml +69 -8
- package/AGENTS.md +21 -0
- package/CHANGELOG.md +69 -0
- package/CLAUDE.md +13 -0
- package/CLAUDE_CODE_AGENT_INFINITE.md +5 -3
- package/Dockerfile +4 -0
- package/Dockerfile.full +55 -3
- package/README.md +81 -0
- package/bin/install.js +16 -5
- package/cloudbuild-full.yaml +57 -0
- package/docker-compose.yml +5 -3
- package/docs/OBSERVABILITY.md +164 -0
- package/docs/byoc-agent-image.md +70 -0
- package/docs/graphify-packaging-plan.md +133 -0
- package/docs/graphify-validation-run-2026-10-28.md +243 -0
- package/docs/legacy-connectors.md +268 -0
- package/docs/releasing.md +43 -0
- package/docs/signed-commands.md +163 -0
- package/env.example +30 -0
- package/helm/Chart.yaml +2 -2
- package/helm/templates/deployment.yaml +13 -3
- package/helm/templates/networkpolicy.yaml +22 -0
- package/helm/templates/secret.yaml +1 -0
- package/helm/values.yaml +13 -14
- package/mcp-server.js +4 -1
- package/package.json +12 -4
- package/scripts/sync-fixtures.mjs +246 -0
- package/scripts/upgrade-observability-push.sh +227 -0
- package/src/adapters/as400/as400-parser.js +12 -0
- package/src/adapters/as400/index.js +134 -0
- package/src/adapters/cobol/cobol-parser.js +37 -0
- package/src/adapters/cobol/index.js +151 -0
- package/src/adapters/ibmmq/ibmmq-parser.js +23 -0
- package/src/adapters/ibmmq/index.js +137 -0
- package/src/adapters/index.js +79 -0
- package/src/adapters/oracle-forms/index.js +145 -0
- package/src/adapters/oracle-forms/oracle-forms-parser.js +11 -0
- package/src/adapters/sap/index.js +146 -0
- package/src/adapters/sap/sap-parser.js +41 -0
- package/src/ai-engine.js +22 -22
- package/src/config/trusted-signing-keys.json +3 -0
- package/src/detectors/build-tool-detector.js +50 -0
- package/src/detectors/multi-project-detector.js +22 -22
- package/src/detectors/service-detector.js +24 -24
- package/src/exec-utils.js +26 -4
- package/src/git/base-git-provider.js +117 -117
- package/src/git/bitbucket-provider.js +2 -2
- package/src/git/git-provider-registry.js +18 -18
- package/src/git/github-provider.js +13 -13
- package/src/git/multi-repo-pr.js +161 -0
- package/src/mcp-http-server.js +32 -16
- package/src/observability/bundle-files.js +418 -0
- package/src/observability/detect.js +151 -0
- package/src/observability/provision.js +212 -0
- package/src/observability/render-cli.js +59 -0
- package/src/observability/routes.js +88 -0
- package/src/patch/patch-engine.js +28 -28
- package/src/patch-manager.js +85 -85
- package/src/patch_bkp.js +14 -14
- package/src/ports.js +4 -4
- package/src/routes/workspace.route.js +47 -16
- package/src/runtimes/base-runtime.js +75 -75
- package/src/runtimes/java/gradle-runtime.js +17 -17
- package/src/runtimes/java/java-integrations.js +25 -25
- package/src/runtimes/java/maven-runtime.js +34 -34
- package/src/runtimes/node/node-integrations.js +1 -1
- package/src/runtimes/node/npm-runtime.js +14 -14
- package/src/runtimes/node/yarn-runtime.js +4 -4
- package/src/runtimes/runtime-registry.js +25 -25
- package/src/server-local.js +4 -4
- package/src/server.js +1412 -335
- package/src/siblings/burst-suppressor.js +89 -0
- package/src/siblings/capabilities.js +273 -0
- package/src/siblings/env-compat.cjs +58 -0
- package/src/siblings/incident-forwarder.js +320 -0
- package/src/siblings/logger.js +15 -0
- package/src/siblings/metrics.js +94 -0
- package/src/utils/agent-auth.js +184 -0
- package/src/utils/approval-token.js +56 -0
- package/src/utils/bind-host.js +29 -0
- package/src/utils/branch-refspec.js +98 -0
- package/src/utils/child-env.js +34 -0
- package/src/utils/environment-diagnostics.js +55 -55
- package/src/utils/exec-policy.js +962 -0
- package/src/utils/exec-utils.js +15 -15
- package/src/utils/file-count.js +46 -0
- package/src/utils/fs-utils.js +10 -10
- package/src/utils/multi-repo-open.js +132 -0
- package/src/utils/open-gate.js +183 -0
- package/src/utils/ranged-read.js +65 -0
- package/src/utils/request-guards.js +77 -0
- package/src/utils/request-signing.js +342 -0
- package/src/utils/root-recovery.js +244 -0
- package/src/utils/safe-exec-args.js +58 -0
- package/src/utils/sandbox.js +182 -0
- package/src/utils/search-outcome.js +56 -0
- package/src/utils/workspace-root.js +80 -0
- package/src/validators/syntax-validator.js +149 -0
- package/src/workspace/detect-port.js +16 -16
- package/src/workspace/file-reader.js +1 -1
- package/src/workspace/process-manager.js +46 -46
- package/src/workspace-manager.js +86 -24
- package/test/adapters/as400/as400.test.js +121 -0
- package/test/adapters/cobol/cobol.test.js +217 -0
- package/test/adapters/ibmmq/ibmmq.test.js +122 -0
- package/test/adapters/oracle-forms/oracle-forms.test.js +180 -0
- package/test/adapters/registry.test.js +108 -0
- package/test/adapters/sap/sap.test.js +142 -0
- package/test/agent-auth.test.js +194 -0
- package/test/bind-host.test.js +26 -0
- package/test/branch-refspec.test.js +224 -0
- package/test/burst-suppressor.test.js +60 -0
- package/test/capabilities-access.test.js +47 -0
- package/test/capabilities.test.js +87 -0
- package/test/child-env.test.js +23 -0
- package/test/env-compat.test.js +71 -0
- package/test/exec-policy.test.js +463 -0
- package/test/file-count.test.js +64 -0
- package/test/incident-forwarder.test.js +231 -0
- package/test/multi-repo-open.test.js +180 -0
- package/test/multi-repo-pr.test.js +129 -0
- package/test/observability-detect.test.js +199 -0
- package/test/open-gate.test.js +341 -0
- package/test/ranged-read.test.js +60 -0
- package/test/request-guards.test.js +91 -0
- package/test/request-signing.test.js +284 -0
- package/test/root-recovery-sidecar.test.js +171 -0
- package/test/root-recovery.test.js +274 -0
- package/test/safe-exec-args.test.js +58 -0
- package/test/sandbox.test.js +98 -0
- package/test/search-outcome.test.js +50 -0
- package/test/workspace-root.test.js +64 -0
- package/tools/graphify-validation/README.md +92 -0
- package/tools/graphify-validation/config/questions.json +31 -0
- package/tools/graphify-validation/config/repos.json +9 -0
- package/tools/graphify-validation/config/symbols.json +6 -0
- package/tools/graphify-validation/fixtures/spring/expected.json +34 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AppConfig.java +29 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/AuditLogger.java +15 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/Cache.java +7 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/CheckoutService.java +25 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/InMemoryCache.java +18 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/OrderController.java +22 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaymentGateway.java +5 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/PaypalGateway.java +11 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/RefundService.java +14 -0
- package/tools/graphify-validation/fixtures/spring/src/main/java/com/example/fx/StripeGateway.java +11 -0
- package/tools/graphify-validation/lib/affected_vs_rg.py +81 -0
- package/tools/graphify-validation/lib/audit_hook/sitecustomize.py +22 -0
- package/tools/graphify-validation/lib/graph_metrics.py +96 -0
- package/tools/graphify-validation/lib/gv_common.py +130 -0
- package/tools/graphify-validation/lib/licenses.py +47 -0
- package/tools/graphify-validation/lib/pip_audit_summary.py +18 -0
- package/tools/graphify-validation/lib/recall.py +115 -0
- package/tools/graphify-validation/lib/report.py +226 -0
- package/tools/graphify-validation/lib/run_all.py +237 -0
- package/tools/graphify-validation/lib/run_graphify.py +144 -0
- package/tools/graphify-validation/lib/rusage_helper.py +17 -0
- package/tools/graphify-validation/lib/sample_edges.py +246 -0
- package/tools/graphify-validation/lib/spring_fixture.py +81 -0
- package/tools/graphify-validation/requirements-py311.lock +508 -0
- package/tools/graphify-validation/requirements-tools.in +1 -0
- package/tools/graphify-validation/requirements-tools.lock +371 -0
- package/tools/graphify-validation/requirements.in +1 -0
- package/tools/graphify-validation/requirements.lock +502 -0
- package/tools/graphify-validation/run.sh +43 -0
- package/tools/graphify-validation/tests/test_harness.py +151 -0
- package/cloudbuild-agent-qa.yaml +0 -43
- package/cloudbuild-agent-staging.yaml +0 -40
- package/cloudbuild.yaml.deprecated +0 -42
- package/src/local-agent-source.tar.gz +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// src/adapters/sap/index.js
|
|
2
|
+
// SapLogAdapter — watches dev_w* and SLOG* files; emits 6-key error events.
|
|
3
|
+
// AD-1 lifecycle, AD-3 polling, AD-4 byte-position dedup, AD-5 per-file ordering.
|
|
4
|
+
|
|
5
|
+
import { EventEmitter } from 'node:events';
|
|
6
|
+
import { stat, open } from 'node:fs/promises';
|
|
7
|
+
import { join } from 'node:path';
|
|
8
|
+
import chokidar from 'chokidar';
|
|
9
|
+
import { SAP_DEV_W_REGEX, SAP_SLOG_REGEX, normalizeDevWMatch, normalizeSlogMatch } from './sap-parser.js';
|
|
10
|
+
|
|
11
|
+
const ADAPTER = 'sap';
|
|
12
|
+
|
|
13
|
+
export class SapLogAdapter extends EventEmitter {
|
|
14
|
+
constructor(config = {}) {
|
|
15
|
+
super();
|
|
16
|
+
this.sapDir = config.sapDir || '/usr/sap/PRD';
|
|
17
|
+
this.instanceNumber = config.instanceNumber || '00';
|
|
18
|
+
this.interval = config.interval ?? 5000;
|
|
19
|
+
this.replayOnStart = config.replayOnStart === true;
|
|
20
|
+
this._watchers = [];
|
|
21
|
+
this._offsets = new Map(); // filePath → byte offset
|
|
22
|
+
this._inodes = new Map(); // filePath → inode
|
|
23
|
+
this._isRunning = false;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
get isRunning() { return this._isRunning; }
|
|
27
|
+
|
|
28
|
+
async start() {
|
|
29
|
+
if (this._isRunning) return;
|
|
30
|
+
this._isRunning = true;
|
|
31
|
+
|
|
32
|
+
const workGlob = join(this.sapDir, `D${this.instanceNumber}`, 'work', 'dev_w*');
|
|
33
|
+
const logGlob = join(this.sapDir, `D${this.instanceNumber}`, 'log', 'SLOG*');
|
|
34
|
+
|
|
35
|
+
const watcherOpts = {
|
|
36
|
+
usePolling: true,
|
|
37
|
+
interval: this.interval,
|
|
38
|
+
ignoreInitial: false, // we handle initial ourselves via TAIL seek
|
|
39
|
+
awaitWriteFinish: false,
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const wWork = chokidar.watch(workGlob, watcherOpts);
|
|
43
|
+
const wSlog = chokidar.watch(logGlob, watcherOpts);
|
|
44
|
+
|
|
45
|
+
const handle = (filePath, kind) => {
|
|
46
|
+
// ENOENT-tolerant: stat() inside _readNew handles disappearance.
|
|
47
|
+
this._readNew(filePath, kind).catch(() => { /* swallow per ENOENT silence rule */ });
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
// 'add' fires for files present at watch start; honor TAIL/replay flag.
|
|
51
|
+
const onAdd = (kind) => async (filePath) => {
|
|
52
|
+
if (this.replayOnStart) {
|
|
53
|
+
this._offsets.set(filePath, 0);
|
|
54
|
+
} else {
|
|
55
|
+
try {
|
|
56
|
+
const st = await stat(filePath);
|
|
57
|
+
this._offsets.set(filePath, st.size);
|
|
58
|
+
this._inodes.set(filePath, st.ino);
|
|
59
|
+
return; // TAIL mode — don't emit pre-existing content
|
|
60
|
+
} catch { return; }
|
|
61
|
+
}
|
|
62
|
+
handle(filePath, kind);
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
wWork.on('add', onAdd('dev_w'));
|
|
66
|
+
wWork.on('change', (fp) => handle(fp, 'dev_w'));
|
|
67
|
+
wSlog.on('add', onAdd('slog'));
|
|
68
|
+
wSlog.on('change', (fp) => handle(fp, 'slog'));
|
|
69
|
+
|
|
70
|
+
// chokidar's 'error' events would otherwise bubble; swallow to keep ENOENT silent.
|
|
71
|
+
wWork.on('error', () => {});
|
|
72
|
+
wSlog.on('error', () => {});
|
|
73
|
+
|
|
74
|
+
this._watchers.push(wWork, wSlog);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async stop() {
|
|
78
|
+
if (!this._isRunning) return;
|
|
79
|
+
this._isRunning = false;
|
|
80
|
+
await Promise.all(this._watchers.map((w) => w.close().catch(() => {})));
|
|
81
|
+
this._watchers = [];
|
|
82
|
+
this._offsets.clear();
|
|
83
|
+
this._inodes.clear();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async _readNew(filePath, kind) {
|
|
87
|
+
let st;
|
|
88
|
+
try { st = await stat(filePath); } catch { return; } // ENOENT silence
|
|
89
|
+
|
|
90
|
+
const lastOffset = this._offsets.get(filePath) ?? 0;
|
|
91
|
+
const lastInode = this._inodes.get(filePath);
|
|
92
|
+
|
|
93
|
+
// Rotation: inode changed OR size shrunk → reset to 0
|
|
94
|
+
if ((lastInode !== undefined && st.ino !== lastInode) || st.size < lastOffset) {
|
|
95
|
+
this._offsets.set(filePath, 0);
|
|
96
|
+
this._inodes.set(filePath, st.ino);
|
|
97
|
+
return this._readNew(filePath, kind); // re-enter with offset=0
|
|
98
|
+
}
|
|
99
|
+
this._inodes.set(filePath, st.ino);
|
|
100
|
+
|
|
101
|
+
if (st.size <= lastOffset) return;
|
|
102
|
+
|
|
103
|
+
const fh = await open(filePath, 'r');
|
|
104
|
+
try {
|
|
105
|
+
const len = st.size - lastOffset;
|
|
106
|
+
const buf = Buffer.alloc(len);
|
|
107
|
+
await fh.read(buf, 0, len, lastOffset);
|
|
108
|
+
this._offsets.set(filePath, st.size);
|
|
109
|
+
// Tolerate CRLF and LF; split, drop trailing empty (no event for blank lines per AD-6 SAP empty-line trap).
|
|
110
|
+
const text = buf.toString('utf8');
|
|
111
|
+
const lines = text.split(/\r?\n/);
|
|
112
|
+
const sourceDir = filePath.split('/').slice(0, -1).join('/');
|
|
113
|
+
for (const line of lines) {
|
|
114
|
+
if (line.trim().length === 0) continue;
|
|
115
|
+
this._tryEmit(line, filePath, sourceDir, kind);
|
|
116
|
+
}
|
|
117
|
+
} finally {
|
|
118
|
+
await fh.close();
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
_tryEmit(line, filePath, sourceDir, kind) {
|
|
123
|
+
let errorCode = null;
|
|
124
|
+
if (kind === 'dev_w') {
|
|
125
|
+
const m = line.match(SAP_DEV_W_REGEX);
|
|
126
|
+
errorCode = normalizeDevWMatch(m);
|
|
127
|
+
} else if (kind === 'slog') {
|
|
128
|
+
const m = line.match(SAP_SLOG_REGEX);
|
|
129
|
+
errorCode = normalizeSlogMatch(m);
|
|
130
|
+
}
|
|
131
|
+
if (errorCode === null) return; // no recognizable code → no event
|
|
132
|
+
|
|
133
|
+
// 6-key frozen event payload (AD-4 contract — extra keys forbidden)
|
|
134
|
+
const event = Object.freeze({
|
|
135
|
+
adapter: ADAPTER,
|
|
136
|
+
errorCode,
|
|
137
|
+
file: filePath,
|
|
138
|
+
message: line,
|
|
139
|
+
source: sourceDir,
|
|
140
|
+
timestamp: new Date().toISOString(),
|
|
141
|
+
});
|
|
142
|
+
this.emit('error', event);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default SapLogAdapter;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
// src/adapters/sap/sap-parser.js
|
|
2
|
+
// T-2-03 mitigation: regexes exported as named constants for isolated testing.
|
|
3
|
+
// Adapter binds chokidar to dev_w* and SLOG* file globs only; these regexes
|
|
4
|
+
// never run against arbitrary text by the adapter itself.
|
|
5
|
+
|
|
6
|
+
// Matches ABAP work-process trace error lines:
|
|
7
|
+
// "M *** ERROR => Runtime error CONVT_NO_NUMBER"
|
|
8
|
+
// "M *** ERROR => Exception condition CX_SY_OPEN_SQL_DB raised"
|
|
9
|
+
// "B *** ERROR => SQL error in SELECT, sy-subrc = 4"
|
|
10
|
+
export const SAP_DEV_W_REGEX = /\*\*\* ERROR =>\s+(?:Runtime error|Exception condition|SQL error in [A-Z]+,)\s*([A-Z_]+(?:[A-Z_0-9]*)|sy-subrc\s*=\s*\d+)/i;
|
|
11
|
+
|
|
12
|
+
// Matches SLOG pipe-delimited error/fatal records:
|
|
13
|
+
// "| 2024-01-15 09:12:44 | E | 04 | Database error..., sy-subrc = 4 |"
|
|
14
|
+
// "| 2024-01-15 14:01:55 | F | 22 | Short dump 4815162343 created |"
|
|
15
|
+
export const SAP_SLOG_REGEX = /^\|\s*\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}:\d{2}\s*\|\s*([EF])\s*\|\s*\d+\s*\|\s*(.+?)\s*\|/;
|
|
16
|
+
|
|
17
|
+
// Normalizes captured payload to errorCode string (or null when no recognizable code).
|
|
18
|
+
export function normalizeDevWMatch(match) {
|
|
19
|
+
if (!match) return null;
|
|
20
|
+
const raw = (match[1] || '').trim();
|
|
21
|
+
if (/^sy-subrc/i.test(raw)) {
|
|
22
|
+
const m = raw.match(/=\s*(\d+)/);
|
|
23
|
+
return m ? `SY-SUBRC=${m[1]}` : 'SY-SUBRC';
|
|
24
|
+
}
|
|
25
|
+
return raw.toUpperCase();
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function normalizeSlogMatch(match) {
|
|
29
|
+
if (!match) return null;
|
|
30
|
+
const body = (match[2] || '').trim();
|
|
31
|
+
// Try to extract a known runtime-error keyword from the SLOG message body
|
|
32
|
+
const m = body.match(/(CONVT_NO_NUMBER|CX_SY_OPEN_SQL_DB|sy-subrc\s*=\s*\d+|Short dump \d+)/i);
|
|
33
|
+
if (!m) return null;
|
|
34
|
+
const cap = m[1].trim();
|
|
35
|
+
if (/^sy-subrc/i.test(cap)) {
|
|
36
|
+
const num = cap.match(/=\s*(\d+)/);
|
|
37
|
+
return num ? `SY-SUBRC=${num[1]}` : 'SY-SUBRC';
|
|
38
|
+
}
|
|
39
|
+
if (/^Short dump/i.test(cap)) return 'SHORT_DUMP';
|
|
40
|
+
return cap.toUpperCase();
|
|
41
|
+
}
|
package/src/ai-engine.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// ============================================
|
|
2
2
|
// 🧠 AI VIBE CODING ENGINE
|
|
3
|
-
//
|
|
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
|
-
*
|
|
19
|
-
*
|
|
18
|
+
* Universal auto-healing system that uses AI to resolve
|
|
19
|
+
* ANY error automatically - not just startup.
|
|
20
20
|
*
|
|
21
21
|
* Features:
|
|
22
|
-
* -
|
|
23
|
-
* -
|
|
22
|
+
* - Real-time error monitoring
|
|
23
|
+
* - Automatic error classification
|
|
24
24
|
* - Quick fixes locais (sem chamar AI)
|
|
25
|
-
* -
|
|
26
|
-
* -
|
|
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
|
-
//
|
|
37
|
+
// History
|
|
38
38
|
this.errorHistory = [];
|
|
39
39
|
this.fixHistory = [];
|
|
40
40
|
this.pendingFixes = [];
|
|
41
41
|
|
|
42
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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:
|
|
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
|
-
//
|
|
238
|
+
// Emit the event
|
|
239
239
|
this.emit('error_detected', errorEntry);
|
|
240
240
|
|
|
241
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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,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
|
+
}
|
|
@@ -9,24 +9,24 @@ import { FileReader } from "../workspace/file-reader.js";
|
|
|
9
9
|
/**
|
|
10
10
|
* MultiProjectDetector
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Detects multiple projects within a workspace
|
|
13
|
+
* Scans subfolders looking for independent projects (pom.xml, package.json, etc)
|
|
14
14
|
*/
|
|
15
15
|
export class MultiProjectDetector {
|
|
16
16
|
constructor(workspaceRoot) {
|
|
17
17
|
this.workspaceRoot = workspaceRoot;
|
|
18
|
-
this.maxDepth = 2; //
|
|
18
|
+
this.maxDepth = 2; // Scan up to 2 levels deep
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
*
|
|
22
|
+
* Detects all projects in the workspace
|
|
23
23
|
*/
|
|
24
24
|
async detectProjects() {
|
|
25
25
|
console.log(`🔍 Scanning workspace for multiple projects: ${this.workspaceRoot}`);
|
|
26
26
|
|
|
27
27
|
const projects = [];
|
|
28
28
|
|
|
29
|
-
// 1️⃣
|
|
29
|
+
// 1️⃣ Check whether the workspace root itself is a project
|
|
30
30
|
if (await this.isProject(this.workspaceRoot)) {
|
|
31
31
|
console.log(`✅ Root is a project`);
|
|
32
32
|
projects.push({
|
|
@@ -36,13 +36,13 @@ export class MultiProjectDetector {
|
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
-
// 2️⃣
|
|
39
|
+
// 2️⃣ Scan subfolders looking for projects
|
|
40
40
|
const subProjects = await this.scanSubdirectories(this.workspaceRoot, 0);
|
|
41
41
|
projects.push(...subProjects);
|
|
42
42
|
|
|
43
43
|
console.log(`✅ Found ${projects.length} project(s) in workspace`);
|
|
44
44
|
|
|
45
|
-
// 3️⃣
|
|
45
|
+
// 3️⃣ Detect services for each project
|
|
46
46
|
const allServices = [];
|
|
47
47
|
for (const project of projects) {
|
|
48
48
|
console.log(`🔎 Detecting services in: ${project.name}`);
|
|
@@ -55,7 +55,7 @@ export class MultiProjectDetector {
|
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
/**
|
|
58
|
-
*
|
|
58
|
+
* Checks whether a directory is a project
|
|
59
59
|
*/
|
|
60
60
|
async isProject(dir) {
|
|
61
61
|
const markers = [
|
|
@@ -67,12 +67,12 @@ export class MultiProjectDetector {
|
|
|
67
67
|
'pyproject.toml', // Python Poetry
|
|
68
68
|
'go.mod', // Go
|
|
69
69
|
'Cargo.toml', // Rust
|
|
70
|
-
'.csproj', // .NET (
|
|
70
|
+
'.csproj', // .NET (any .csproj file)
|
|
71
71
|
'.sln' // .NET solution
|
|
72
72
|
];
|
|
73
73
|
|
|
74
74
|
for (const marker of markers) {
|
|
75
|
-
//
|
|
75
|
+
// For .csproj and .sln, look for any file with that extension
|
|
76
76
|
if (marker.startsWith('.')) {
|
|
77
77
|
try {
|
|
78
78
|
const files = await readdir(dir);
|
|
@@ -83,7 +83,7 @@ export class MultiProjectDetector {
|
|
|
83
83
|
continue;
|
|
84
84
|
}
|
|
85
85
|
} else {
|
|
86
|
-
//
|
|
86
|
+
// For specific markers, check whether they exist
|
|
87
87
|
if (await exists(path.join(dir, marker))) {
|
|
88
88
|
return true;
|
|
89
89
|
}
|
|
@@ -94,7 +94,7 @@ export class MultiProjectDetector {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
/**
|
|
97
|
-
*
|
|
97
|
+
* Scans subdirectories recursively
|
|
98
98
|
*/
|
|
99
99
|
async scanSubdirectories(dir, depth) {
|
|
100
100
|
if (depth >= this.maxDepth) {
|
|
@@ -109,7 +109,7 @@ export class MultiProjectDetector {
|
|
|
109
109
|
for (const entry of entries) {
|
|
110
110
|
if (!entry.isDirectory()) continue;
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// Ignore common folders that are not projects
|
|
113
113
|
const ignoreDirs = [
|
|
114
114
|
'node_modules', 'target', 'build', 'dist', 'out',
|
|
115
115
|
'.git', '.idea', '.vscode', '__pycache__',
|
|
@@ -123,7 +123,7 @@ export class MultiProjectDetector {
|
|
|
123
123
|
|
|
124
124
|
const fullPath = path.join(dir, entry.name);
|
|
125
125
|
|
|
126
|
-
//
|
|
126
|
+
// Check whether it is a project
|
|
127
127
|
if (await this.isProject(fullPath)) {
|
|
128
128
|
console.log(`✅ Found project: ${entry.name}`);
|
|
129
129
|
projects.push({
|
|
@@ -131,11 +131,11 @@ export class MultiProjectDetector {
|
|
|
131
131
|
path: fullPath,
|
|
132
132
|
isRoot: false
|
|
133
133
|
});
|
|
134
|
-
//
|
|
134
|
+
// Do not scan inside projects that were found
|
|
135
135
|
continue;
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
//
|
|
138
|
+
// Keep scanning recursively
|
|
139
139
|
const subProjects = await this.scanSubdirectories(fullPath, depth + 1);
|
|
140
140
|
projects.push(...subProjects);
|
|
141
141
|
}
|
|
@@ -147,19 +147,19 @@ export class MultiProjectDetector {
|
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
*
|
|
150
|
+
* Detects services in a specific project
|
|
151
151
|
*/
|
|
152
152
|
async detectServicesInProject(project) {
|
|
153
153
|
try {
|
|
154
|
-
//
|
|
154
|
+
// Use the scanner to analyze the project
|
|
155
155
|
const scanner = new WorkspaceScanner(project.path);
|
|
156
156
|
const structure = await scanner.scan();
|
|
157
157
|
|
|
158
|
-
//
|
|
158
|
+
// Detect the language
|
|
159
159
|
const languageDetector = new LanguageDetector(structure.files);
|
|
160
160
|
const languageInfo = languageDetector.detect();
|
|
161
161
|
|
|
162
|
-
//
|
|
162
|
+
// Detect the framework
|
|
163
163
|
const fileReader = new FileReader(project.path);
|
|
164
164
|
const frameworkDetector = new FrameworkDetector(
|
|
165
165
|
languageInfo.primary,
|
|
@@ -168,7 +168,7 @@ export class MultiProjectDetector {
|
|
|
168
168
|
);
|
|
169
169
|
const frameworkInfo = await frameworkDetector.detect();
|
|
170
170
|
|
|
171
|
-
//
|
|
171
|
+
// Detect the services
|
|
172
172
|
const serviceDetector = new ServiceDetector(
|
|
173
173
|
project.path,
|
|
174
174
|
languageInfo,
|
|
@@ -177,7 +177,7 @@ export class MultiProjectDetector {
|
|
|
177
177
|
|
|
178
178
|
const services = await serviceDetector.detect();
|
|
179
179
|
|
|
180
|
-
//
|
|
180
|
+
// Add the workspace name to each service
|
|
181
181
|
return services.map(service => ({
|
|
182
182
|
...service,
|
|
183
183
|
workspaceName: project.name,
|