pi-lens 3.6.2 → 3.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -2
- package/package.json +4 -4
- package/tsconfig.json +1 -1
- package/clients/__tests__/file-time.test.js +0 -216
- package/clients/__tests__/file-time.test.ts +0 -276
- package/clients/__tests__/format-service.test.js +0 -245
- package/clients/__tests__/format-service.test.ts +0 -339
- package/clients/__tests__/formatters.test.js +0 -271
- package/clients/__tests__/formatters.test.ts +0 -401
- package/clients/agent-behavior-client.js +0 -110
- package/clients/agent-behavior-client.test.js +0 -94
- package/clients/agent-behavior-client.test.ts +0 -116
- package/clients/amain-types.js +0 -164
- package/clients/architect-client.js +0 -291
- package/clients/ast-grep-client.js +0 -253
- package/clients/ast-grep-parser.js +0 -84
- package/clients/ast-grep-rule-manager.js +0 -89
- package/clients/ast-grep-types.js +0 -9
- package/clients/auto-loop.js +0 -131
- package/clients/biome-client.js +0 -420
- package/clients/biome-client.test.js +0 -144
- package/clients/biome-client.test.ts +0 -163
- package/clients/cache/rule-cache.js +0 -72
- package/clients/cache-manager.js +0 -245
- package/clients/cache-manager.test.js +0 -197
- package/clients/cache-manager.test.ts +0 -299
- package/clients/complexity-client.js +0 -675
- package/clients/complexity-client.test.js +0 -234
- package/clients/complexity-client.test.ts +0 -255
- package/clients/config-validator.js +0 -465
- package/clients/dependency-checker.js +0 -325
- package/clients/dependency-checker.test.js +0 -60
- package/clients/dependency-checker.test.ts +0 -71
- package/clients/dispatch/__tests__/autofix-integration.test.js +0 -245
- package/clients/dispatch/__tests__/autofix-integration.test.ts +0 -300
- package/clients/dispatch/__tests__/runner-registration.test.js +0 -234
- package/clients/dispatch/__tests__/runner-registration.test.ts +0 -286
- package/clients/dispatch/debug.log +0 -1
- package/clients/dispatch/dispatcher.edge.test.js +0 -82
- package/clients/dispatch/dispatcher.edge.test.ts +0 -100
- package/clients/dispatch/dispatcher.format.test.js +0 -46
- package/clients/dispatch/dispatcher.format.test.ts +0 -58
- package/clients/dispatch/dispatcher.inline.test.js +0 -74
- package/clients/dispatch/dispatcher.inline.test.ts +0 -93
- package/clients/dispatch/dispatcher.js +0 -381
- package/clients/dispatch/dispatcher.test.js +0 -116
- package/clients/dispatch/dispatcher.test.ts +0 -149
- package/clients/dispatch/integration.js +0 -108
- package/clients/dispatch/plan.js +0 -183
- package/clients/dispatch/runners/architect.js +0 -83
- package/clients/dispatch/runners/architect.test.js +0 -138
- package/clients/dispatch/runners/architect.test.ts +0 -162
- package/clients/dispatch/runners/ast-grep-napi.js +0 -405
- package/clients/dispatch/runners/ast-grep-napi.test.js +0 -107
- package/clients/dispatch/runners/ast-grep-napi.test.ts +0 -129
- package/clients/dispatch/runners/ast-grep.js +0 -157
- package/clients/dispatch/runners/biome.js +0 -55
- package/clients/dispatch/runners/config-validation.js +0 -67
- package/clients/dispatch/runners/go-vet.js +0 -48
- package/clients/dispatch/runners/index.js +0 -47
- package/clients/dispatch/runners/lsp.js +0 -102
- package/clients/dispatch/runners/oxlint.js +0 -67
- package/clients/dispatch/runners/oxlint.test.js +0 -230
- package/clients/dispatch/runners/oxlint.test.ts +0 -303
- package/clients/dispatch/runners/pyright.js +0 -100
- package/clients/dispatch/runners/pyright.test.js +0 -98
- package/clients/dispatch/runners/pyright.test.ts +0 -121
- package/clients/dispatch/runners/python-slop.js +0 -97
- package/clients/dispatch/runners/python-slop.test.js +0 -203
- package/clients/dispatch/runners/python-slop.test.ts +0 -298
- package/clients/dispatch/runners/ruff.js +0 -48
- package/clients/dispatch/runners/rust-clippy.js +0 -102
- package/clients/dispatch/runners/scan_codebase.test.js +0 -89
- package/clients/dispatch/runners/scan_codebase.test.ts +0 -105
- package/clients/dispatch/runners/shellcheck.js +0 -147
- package/clients/dispatch/runners/shellcheck.test.js +0 -98
- package/clients/dispatch/runners/shellcheck.test.ts +0 -129
- package/clients/dispatch/runners/similarity.js +0 -230
- package/clients/dispatch/runners/spellcheck.js +0 -106
- package/clients/dispatch/runners/spellcheck.test.js +0 -158
- package/clients/dispatch/runners/spellcheck.test.ts +0 -214
- package/clients/dispatch/runners/tree-sitter.js +0 -246
- package/clients/dispatch/runners/ts-lsp.js +0 -125
- package/clients/dispatch/runners/ts-slop.js +0 -113
- package/clients/dispatch/runners/type-safety.js +0 -142
- package/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -134
- package/clients/dispatch/runners/utils/runner-helpers.js +0 -115
- package/clients/dispatch/runners/utils.js +0 -51
- package/clients/dispatch/runners/yaml-rule-parser.js +0 -360
- package/clients/dispatch/types.js +0 -16
- package/clients/dispatch/utils/format-utils.js +0 -44
- package/clients/dogfood.test.js +0 -201
- package/clients/dogfood.test.ts +0 -269
- package/clients/file-kinds.js +0 -177
- package/clients/file-kinds.test.js +0 -169
- package/clients/file-kinds.test.ts +0 -210
- package/clients/file-time.js +0 -152
- package/clients/file-utils.js +0 -40
- package/clients/fix-scanners.js +0 -204
- package/clients/format-service.js +0 -184
- package/clients/formatters.js +0 -488
- package/clients/go-client.js +0 -203
- package/clients/go-client.test.js +0 -127
- package/clients/go-client.test.ts +0 -143
- package/clients/installer/index.js +0 -403
- package/clients/interviewer-templates.js +0 -75
- package/clients/interviewer.js +0 -173
- package/clients/jscpd-client.js +0 -196
- package/clients/jscpd-client.test.js +0 -127
- package/clients/jscpd-client.test.ts +0 -145
- package/clients/knip-client.js +0 -239
- package/clients/knip-client.test.js +0 -112
- package/clients/knip-client.test.ts +0 -128
- package/clients/latency-logger.js +0 -40
- package/clients/lsp/__tests__/client.test.js +0 -310
- package/clients/lsp/__tests__/client.test.ts +0 -412
- package/clients/lsp/__tests__/config.test.js +0 -167
- package/clients/lsp/__tests__/config.test.ts +0 -217
- package/clients/lsp/__tests__/error-recovery.test.js +0 -213
- package/clients/lsp/__tests__/error-recovery.test.ts +0 -279
- package/clients/lsp/__tests__/integration.test.js +0 -127
- package/clients/lsp/__tests__/integration.test.ts +0 -160
- package/clients/lsp/__tests__/launch.test.js +0 -313
- package/clients/lsp/__tests__/launch.test.ts +0 -394
- package/clients/lsp/__tests__/server.test.js +0 -259
- package/clients/lsp/__tests__/server.test.ts +0 -332
- package/clients/lsp/__tests__/service.test.js +0 -438
- package/clients/lsp/__tests__/service.test.ts +0 -530
- package/clients/lsp/client.js +0 -350
- package/clients/lsp/config.js +0 -112
- package/clients/lsp/index.js +0 -318
- package/clients/lsp/installer/index.js +0 -391
- package/clients/lsp/interactive-install.js +0 -221
- package/clients/lsp/language.js +0 -170
- package/clients/lsp/launch.js +0 -329
- package/clients/lsp/lsp/launch.js +0 -116
- package/clients/lsp/lsp/server.js +0 -532
- package/clients/lsp/lsp-index.js +0 -10
- package/clients/lsp/path-utils.js +0 -5
- package/clients/lsp/server.js +0 -725
- package/clients/lsp/test-py-spawn/requirements.txt +0 -1
- package/clients/lsp/test-py-spawn/test.py +0 -3
- package/clients/lsp/test-py-svc/requirements.txt +0 -1
- package/clients/lsp/test-py-svc/test.py +0 -3
- package/clients/lsp/test-python-project/requirements.txt +0 -1
- package/clients/lsp/test-python-project/test.py +0 -5
- package/clients/metrics-client.js +0 -107
- package/clients/metrics-client.test.js +0 -128
- package/clients/metrics-client.test.ts +0 -163
- package/clients/metrics-history.js +0 -367
- package/clients/path-utils.js +0 -142
- package/clients/pipeline.js +0 -272
- package/clients/production-readiness.js +0 -522
- package/clients/project-index.js +0 -255
- package/clients/project-metadata.js +0 -531
- package/clients/ruff-client.js +0 -325
- package/clients/ruff-client.test.js +0 -132
- package/clients/ruff-client.test.ts +0 -153
- package/clients/rules-scanner.js +0 -97
- package/clients/runner-tracker.js +0 -152
- package/clients/rust-client.js +0 -205
- package/clients/rust-client.test.js +0 -108
- package/clients/rust-client.test.ts +0 -130
- package/clients/safe-spawn-async.js +0 -163
- package/clients/safe-spawn.js +0 -241
- package/clients/sanitize.js +0 -291
- package/clients/sanitize.test.js +0 -177
- package/clients/sanitize.test.ts +0 -223
- package/clients/scan-architectural-debt.js +0 -167
- package/clients/scan-utils.js +0 -83
- package/clients/secrets-scanner.js +0 -119
- package/clients/secrets-scanner.test.js +0 -100
- package/clients/secrets-scanner.test.ts +0 -113
- package/clients/sg-runner.js +0 -292
- package/clients/state-matrix.js +0 -160
- package/clients/subprocess-client.js +0 -65
- package/clients/symbol-types.js +0 -5
- package/clients/test-runner-client.js +0 -523
- package/clients/test-runner-client.test.js +0 -192
- package/clients/test-runner-client.test.ts +0 -253
- package/clients/test-utils.js +0 -27
- package/clients/test-utils.ts +0 -36
- package/clients/todo-scanner.js +0 -200
- package/clients/todo-scanner.test.js +0 -301
- package/clients/todo-scanner.test.ts +0 -352
- package/clients/tool-availability.js +0 -207
- package/clients/tree-sitter-client.js +0 -601
- package/clients/tree-sitter-query-loader.js +0 -355
- package/clients/tree-sitter-symbol-extractor.js +0 -289
- package/clients/ts-service.js +0 -129
- package/clients/type-coverage-client.js +0 -127
- package/clients/type-coverage-client.test.js +0 -105
- package/clients/type-coverage-client.test.ts +0 -125
- package/clients/type-safety-client.js +0 -138
- package/clients/types.js +0 -11
- package/clients/typescript-client.codefix.test.js +0 -157
- package/clients/typescript-client.codefix.test.ts +0 -186
- package/clients/typescript-client.js +0 -509
- package/clients/typescript-client.test.js +0 -105
- package/clients/typescript-client.test.ts +0 -126
- package/commands/booboo.js +0 -1007
- package/commands/fix-from-booboo.js +0 -398
- package/commands/fix-simplified.js +0 -618
- package/commands/rate.js +0 -281
- package/commands/rate.test.js +0 -119
- package/commands/rate.test.ts +0 -131
- package/commands/refactor.js +0 -130
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AgentBehaviorClient for pi-lens
|
|
3
|
-
*
|
|
4
|
-
* Tracks tool call sequences and flags anti-patterns in real-time:
|
|
5
|
-
* - Blind writes: editing or writing without reading first
|
|
6
|
-
* - Thrashing: repeated identical tool calls with no progress
|
|
7
|
-
*
|
|
8
|
-
* No external dependencies — purely tracks tool call history.
|
|
9
|
-
*/
|
|
10
|
-
// --- Constants ---
|
|
11
|
-
const WRITE_OPS = new Set(["edit", "write", "multiedit"]);
|
|
12
|
-
const READ_OPS = new Set(["read", "bash", "grep", "glob", "find", "rg"]);
|
|
13
|
-
const BLIND_WRITE_WINDOW = 5; // Check last N tool calls for a read
|
|
14
|
-
const THRASH_THRESHOLD = 3; // Flag after N consecutive identical tools
|
|
15
|
-
const THRASH_TIMEOUT_MS = 30000; // Reset thrash counter if gap > 30s
|
|
16
|
-
// --- Client ---
|
|
17
|
-
export class AgentBehaviorClient {
|
|
18
|
-
constructor() {
|
|
19
|
-
this.toolHistory = [];
|
|
20
|
-
this.consecutiveCount = 0;
|
|
21
|
-
this.lastToolName = null;
|
|
22
|
-
this.lastToolTimestamp = 0;
|
|
23
|
-
// Per-file tracking
|
|
24
|
-
this.fileEditCount = new Map();
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Record a tool call and return any warnings triggered.
|
|
28
|
-
* Called from tool_result handler.
|
|
29
|
-
*/
|
|
30
|
-
recordToolCall(toolName, filePath) {
|
|
31
|
-
const warnings = [];
|
|
32
|
-
const now = Date.now();
|
|
33
|
-
// Track consecutive identical tools (thrashing)
|
|
34
|
-
if (toolName === this.lastToolName &&
|
|
35
|
-
now - this.lastToolTimestamp < THRASH_TIMEOUT_MS) {
|
|
36
|
-
this.consecutiveCount++;
|
|
37
|
-
}
|
|
38
|
-
else {
|
|
39
|
-
this.consecutiveCount = 1;
|
|
40
|
-
}
|
|
41
|
-
this.lastToolName = toolName;
|
|
42
|
-
this.lastToolTimestamp = now;
|
|
43
|
-
// Check for thrashing
|
|
44
|
-
if (this.consecutiveCount === THRASH_THRESHOLD) {
|
|
45
|
-
warnings.push({
|
|
46
|
-
type: "thrashing",
|
|
47
|
-
message: `🔴 THRASHING — ${THRASH_THRESHOLD} consecutive \`${toolName}\` calls with no other action. Consider fixing the root cause instead of re-running.`,
|
|
48
|
-
severity: "error",
|
|
49
|
-
details: {
|
|
50
|
-
toolName,
|
|
51
|
-
callCount: this.consecutiveCount,
|
|
52
|
-
},
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
|
-
// Check for blind writes
|
|
56
|
-
if (WRITE_OPS.has(toolName)) {
|
|
57
|
-
const recentWindow = this.toolHistory.slice(-BLIND_WRITE_WINDOW);
|
|
58
|
-
const hasRecentRead = recentWindow.some((r) => READ_OPS.has(r.toolName));
|
|
59
|
-
if (!hasRecentRead && recentWindow.length > 0) {
|
|
60
|
-
// Count how many writes in the window without reads
|
|
61
|
-
const writesWithoutRead = recentWindow.filter((r) => WRITE_OPS.has(r.toolName)).length;
|
|
62
|
-
if (writesWithoutRead >= 2) {
|
|
63
|
-
warnings.push({
|
|
64
|
-
type: "blind-write",
|
|
65
|
-
message: `⚠ BLIND WRITE — editing \`${filePath ?? "file"}\` without reading in the last ${BLIND_WRITE_WINDOW} tool calls. Read the file first to avoid assumptions.`,
|
|
66
|
-
severity: "warning",
|
|
67
|
-
details: {
|
|
68
|
-
filePath,
|
|
69
|
-
windowSize: recentWindow.length,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// Track edits per file
|
|
75
|
-
if (filePath) {
|
|
76
|
-
this.fileEditCount.set(filePath, (this.fileEditCount.get(filePath) ?? 0) + 1);
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
// Add to history (keep last 50 entries)
|
|
80
|
-
this.toolHistory.push({ toolName, filePath, timestamp: now });
|
|
81
|
-
if (this.toolHistory.length > 50) {
|
|
82
|
-
this.toolHistory = this.toolHistory.slice(-50);
|
|
83
|
-
}
|
|
84
|
-
return warnings;
|
|
85
|
-
}
|
|
86
|
-
/**
|
|
87
|
-
* Format warnings for LLM consumption.
|
|
88
|
-
*/
|
|
89
|
-
formatWarnings(warnings) {
|
|
90
|
-
if (warnings.length === 0)
|
|
91
|
-
return "";
|
|
92
|
-
return warnings.map((w) => w.message).join("\n");
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Get edit count for a file in this session.
|
|
96
|
-
*/
|
|
97
|
-
getEditCount(filePath) {
|
|
98
|
-
return this.fileEditCount.get(filePath) ?? 0;
|
|
99
|
-
}
|
|
100
|
-
/**
|
|
101
|
-
* Reset state (e.g., on session start).
|
|
102
|
-
*/
|
|
103
|
-
reset() {
|
|
104
|
-
this.toolHistory = [];
|
|
105
|
-
this.consecutiveCount = 0;
|
|
106
|
-
this.lastToolName = null;
|
|
107
|
-
this.lastToolTimestamp = 0;
|
|
108
|
-
this.fileEditCount.clear();
|
|
109
|
-
}
|
|
110
|
-
}
|
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { AgentBehaviorClient } from "./agent-behavior-client.js";
|
|
3
|
-
describe("AgentBehaviorClient", () => {
|
|
4
|
-
let client;
|
|
5
|
-
beforeEach(() => {
|
|
6
|
-
client = new AgentBehaviorClient();
|
|
7
|
-
client.reset();
|
|
8
|
-
});
|
|
9
|
-
describe("blind write detection", () => {
|
|
10
|
-
it("should NOT warn when read precedes write", () => {
|
|
11
|
-
client.recordToolCall("read", "src/file.ts");
|
|
12
|
-
client.recordToolCall("edit", "src/file.ts");
|
|
13
|
-
const warnings = client.recordToolCall("write", "src/file.ts");
|
|
14
|
-
expect(warnings).toHaveLength(0);
|
|
15
|
-
});
|
|
16
|
-
it("should warn when multiple writes happen without reads", () => {
|
|
17
|
-
// First write is OK (no history)
|
|
18
|
-
client.recordToolCall("write", "src/file1.ts");
|
|
19
|
-
// Second write - still in window, accumulates
|
|
20
|
-
client.recordToolCall("edit", "src/file2.ts");
|
|
21
|
-
// Third write without any read - now we have a pattern
|
|
22
|
-
const warnings = client.recordToolCall("edit", "src/file3.ts");
|
|
23
|
-
expect(warnings).toHaveLength(1);
|
|
24
|
-
expect(warnings[0].type).toBe("blind-write");
|
|
25
|
-
});
|
|
26
|
-
it("should not warn for single write with no history", () => {
|
|
27
|
-
const warnings = client.recordToolCall("write", "src/file.ts");
|
|
28
|
-
expect(warnings).toHaveLength(0);
|
|
29
|
-
});
|
|
30
|
-
});
|
|
31
|
-
describe("thrashing detection", () => {
|
|
32
|
-
it("should warn after 3 consecutive identical tool calls", () => {
|
|
33
|
-
client.recordToolCall("bash", undefined);
|
|
34
|
-
// Second call - no warning yet
|
|
35
|
-
let warnings = client.recordToolCall("bash", undefined);
|
|
36
|
-
expect(warnings).toHaveLength(0);
|
|
37
|
-
// Third consecutive - should warn
|
|
38
|
-
warnings = client.recordToolCall("bash", undefined);
|
|
39
|
-
expect(warnings).toHaveLength(1);
|
|
40
|
-
expect(warnings[0].type).toBe("thrashing");
|
|
41
|
-
expect(warnings[0].details.callCount).toBe(3);
|
|
42
|
-
});
|
|
43
|
-
it("should NOT warn for different tool calls", () => {
|
|
44
|
-
client.recordToolCall("read", "src/file.ts");
|
|
45
|
-
client.recordToolCall("bash", "npm test");
|
|
46
|
-
const warnings = client.recordToolCall("edit", "src/file.ts");
|
|
47
|
-
expect(warnings).toHaveLength(0);
|
|
48
|
-
});
|
|
49
|
-
it("should reset count when tool changes", () => {
|
|
50
|
-
client.recordToolCall("bash", undefined);
|
|
51
|
-
client.recordToolCall("bash", undefined);
|
|
52
|
-
// Different tool resets the count
|
|
53
|
-
client.recordToolCall("read", "src/file.ts");
|
|
54
|
-
// Now start new consecutive sequence
|
|
55
|
-
client.recordToolCall("bash", undefined);
|
|
56
|
-
const warnings = client.recordToolCall("bash", undefined);
|
|
57
|
-
expect(warnings).toHaveLength(0); // Only 2 consecutive, not 3
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
|
-
describe("edit counting", () => {
|
|
61
|
-
it("should track edit count per file", () => {
|
|
62
|
-
client.recordToolCall("edit", "src/a.ts");
|
|
63
|
-
client.recordToolCall("edit", "src/a.ts");
|
|
64
|
-
client.recordToolCall("edit", "src/b.ts");
|
|
65
|
-
expect(client.getEditCount("src/a.ts")).toBe(2);
|
|
66
|
-
expect(client.getEditCount("src/b.ts")).toBe(1);
|
|
67
|
-
expect(client.getEditCount("src/c.ts")).toBe(0);
|
|
68
|
-
});
|
|
69
|
-
});
|
|
70
|
-
describe("formatWarnings", () => {
|
|
71
|
-
it("should format multiple warnings", () => {
|
|
72
|
-
const warnings = [
|
|
73
|
-
{
|
|
74
|
-
type: "blind-write",
|
|
75
|
-
message: "⚠ BLIND WRITE — editing file",
|
|
76
|
-
severity: "warning",
|
|
77
|
-
details: {},
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
type: "thrashing",
|
|
81
|
-
message: "🔴 THRASHING — 3 consecutive calls",
|
|
82
|
-
severity: "error",
|
|
83
|
-
details: {},
|
|
84
|
-
},
|
|
85
|
-
];
|
|
86
|
-
const formatted = client.formatWarnings(warnings);
|
|
87
|
-
expect(formatted).toContain("BLIND WRITE");
|
|
88
|
-
expect(formatted).toContain("THRASHING");
|
|
89
|
-
});
|
|
90
|
-
it("should return empty string for no warnings", () => {
|
|
91
|
-
expect(client.formatWarnings([])).toBe("");
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
@@ -1,116 +0,0 @@
|
|
|
1
|
-
import { beforeEach, describe, expect, it } from "vitest";
|
|
2
|
-
import { AgentBehaviorClient } from "./agent-behavior-client.js";
|
|
3
|
-
|
|
4
|
-
describe("AgentBehaviorClient", () => {
|
|
5
|
-
let client: AgentBehaviorClient;
|
|
6
|
-
|
|
7
|
-
beforeEach(() => {
|
|
8
|
-
client = new AgentBehaviorClient();
|
|
9
|
-
client.reset();
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
describe("blind write detection", () => {
|
|
13
|
-
it("should NOT warn when read precedes write", () => {
|
|
14
|
-
client.recordToolCall("read", "src/file.ts");
|
|
15
|
-
client.recordToolCall("edit", "src/file.ts");
|
|
16
|
-
|
|
17
|
-
const warnings = client.recordToolCall("write", "src/file.ts");
|
|
18
|
-
expect(warnings).toHaveLength(0);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("should warn when multiple writes happen without reads", () => {
|
|
22
|
-
// First write is OK (no history)
|
|
23
|
-
client.recordToolCall("write", "src/file1.ts");
|
|
24
|
-
|
|
25
|
-
// Second write - still in window, accumulates
|
|
26
|
-
client.recordToolCall("edit", "src/file2.ts");
|
|
27
|
-
|
|
28
|
-
// Third write without any read - now we have a pattern
|
|
29
|
-
const warnings = client.recordToolCall("edit", "src/file3.ts");
|
|
30
|
-
expect(warnings).toHaveLength(1);
|
|
31
|
-
expect(warnings[0].type).toBe("blind-write");
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
it("should not warn for single write with no history", () => {
|
|
35
|
-
const warnings = client.recordToolCall("write", "src/file.ts");
|
|
36
|
-
expect(warnings).toHaveLength(0);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
describe("thrashing detection", () => {
|
|
41
|
-
it("should warn after 3 consecutive identical tool calls", () => {
|
|
42
|
-
client.recordToolCall("bash", undefined);
|
|
43
|
-
|
|
44
|
-
// Second call - no warning yet
|
|
45
|
-
let warnings = client.recordToolCall("bash", undefined);
|
|
46
|
-
expect(warnings).toHaveLength(0);
|
|
47
|
-
|
|
48
|
-
// Third consecutive - should warn
|
|
49
|
-
warnings = client.recordToolCall("bash", undefined);
|
|
50
|
-
expect(warnings).toHaveLength(1);
|
|
51
|
-
expect(warnings[0].type).toBe("thrashing");
|
|
52
|
-
expect(warnings[0].details.callCount).toBe(3);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("should NOT warn for different tool calls", () => {
|
|
56
|
-
client.recordToolCall("read", "src/file.ts");
|
|
57
|
-
client.recordToolCall("bash", "npm test");
|
|
58
|
-
|
|
59
|
-
const warnings = client.recordToolCall("edit", "src/file.ts");
|
|
60
|
-
expect(warnings).toHaveLength(0);
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
it("should reset count when tool changes", () => {
|
|
64
|
-
client.recordToolCall("bash", undefined);
|
|
65
|
-
client.recordToolCall("bash", undefined);
|
|
66
|
-
|
|
67
|
-
// Different tool resets the count
|
|
68
|
-
client.recordToolCall("read", "src/file.ts");
|
|
69
|
-
|
|
70
|
-
// Now start new consecutive sequence
|
|
71
|
-
client.recordToolCall("bash", undefined);
|
|
72
|
-
|
|
73
|
-
const warnings = client.recordToolCall("bash", undefined);
|
|
74
|
-
expect(warnings).toHaveLength(0); // Only 2 consecutive, not 3
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe("edit counting", () => {
|
|
79
|
-
it("should track edit count per file", () => {
|
|
80
|
-
client.recordToolCall("edit", "src/a.ts");
|
|
81
|
-
client.recordToolCall("edit", "src/a.ts");
|
|
82
|
-
client.recordToolCall("edit", "src/b.ts");
|
|
83
|
-
|
|
84
|
-
expect(client.getEditCount("src/a.ts")).toBe(2);
|
|
85
|
-
expect(client.getEditCount("src/b.ts")).toBe(1);
|
|
86
|
-
expect(client.getEditCount("src/c.ts")).toBe(0);
|
|
87
|
-
});
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
describe("formatWarnings", () => {
|
|
91
|
-
it("should format multiple warnings", () => {
|
|
92
|
-
const warnings = [
|
|
93
|
-
{
|
|
94
|
-
type: "blind-write" as const,
|
|
95
|
-
message: "⚠ BLIND WRITE — editing file",
|
|
96
|
-
severity: "warning" as const,
|
|
97
|
-
details: {},
|
|
98
|
-
},
|
|
99
|
-
{
|
|
100
|
-
type: "thrashing" as const,
|
|
101
|
-
message: "🔴 THRASHING — 3 consecutive calls",
|
|
102
|
-
severity: "error" as const,
|
|
103
|
-
details: {},
|
|
104
|
-
},
|
|
105
|
-
];
|
|
106
|
-
|
|
107
|
-
const formatted = client.formatWarnings(warnings);
|
|
108
|
-
expect(formatted).toContain("BLIND WRITE");
|
|
109
|
-
expect(formatted).toContain("THRASHING");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("should return empty string for no warnings", () => {
|
|
113
|
-
expect(client.formatWarnings([])).toBe("");
|
|
114
|
-
});
|
|
115
|
-
});
|
|
116
|
-
});
|
package/clients/amain-types.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Amain State Types: 57 syntax types + 15 token types
|
|
3
|
-
*
|
|
4
|
-
* Adapted from Amain paper's Java AST structure for TypeScript.
|
|
5
|
-
* 57 syntax types (non-leaf AST nodes) + 15 token types (leaf categories) = 72 states
|
|
6
|
-
*
|
|
7
|
-
* Reference: https://github.com/CGCL-codes/Amain (ASE 2022)
|
|
8
|
-
*/
|
|
9
|
-
import * as ts from "typescript";
|
|
10
|
-
// ============================================================================
|
|
11
|
-
// 57 Syntax Types (non-leaf AST nodes, indices 0-56)
|
|
12
|
-
// ============================================================================
|
|
13
|
-
export const SYNTAX_TYPES = [
|
|
14
|
-
// Declarations (0-9)
|
|
15
|
-
"FunctionDeclaration", // 0
|
|
16
|
-
"ArrowFunction", // 1
|
|
17
|
-
"FunctionExpression", // 2
|
|
18
|
-
"ClassDeclaration", // 3
|
|
19
|
-
"InterfaceDeclaration", // 4
|
|
20
|
-
"TypeAliasDeclaration", // 5
|
|
21
|
-
"EnumDeclaration", // 6
|
|
22
|
-
"MethodDeclaration", // 7
|
|
23
|
-
"Constructor", // 8
|
|
24
|
-
"GetAccessor", // 9
|
|
25
|
-
// More declarations (10-19)
|
|
26
|
-
"SetAccessor", // 10
|
|
27
|
-
"PropertyDeclaration", // 11
|
|
28
|
-
"Parameter", // 12
|
|
29
|
-
"VariableDeclaration", // 13
|
|
30
|
-
"ModuleDeclaration", // 14
|
|
31
|
-
"ImportDeclaration", // 15
|
|
32
|
-
"ExportDeclaration", // 16
|
|
33
|
-
"NamespaceExportDeclaration", // 17
|
|
34
|
-
"ImportClause", // 18
|
|
35
|
-
"NamespaceImport", // 19
|
|
36
|
-
// Statements (20-39)
|
|
37
|
-
"IfStatement", // 20
|
|
38
|
-
"ForStatement", // 21
|
|
39
|
-
"ForOfStatement", // 22
|
|
40
|
-
"ForInStatement", // 23
|
|
41
|
-
"WhileStatement", // 24
|
|
42
|
-
"DoWhileStatement", // 25
|
|
43
|
-
"SwitchStatement", // 26
|
|
44
|
-
"CaseClause", // 27
|
|
45
|
-
"DefaultClause", // 28
|
|
46
|
-
"TryStatement", // 29
|
|
47
|
-
"CatchClause", // 30
|
|
48
|
-
"ThrowStatement", // 31
|
|
49
|
-
"ReturnStatement", // 32
|
|
50
|
-
"BreakStatement", // 33
|
|
51
|
-
"ContinueStatement", // 34
|
|
52
|
-
"Block", // 35
|
|
53
|
-
"EmptyStatement", // 36
|
|
54
|
-
// More statements (37-39)
|
|
55
|
-
"DebuggerStatement", // 37
|
|
56
|
-
"LabeledStatement", // 38
|
|
57
|
-
"WithStatement", // 39
|
|
58
|
-
// Expressions (40-56)
|
|
59
|
-
"BinaryExpression", // 40
|
|
60
|
-
"UnaryExpression", // 41
|
|
61
|
-
"PrefixUnaryExpression", // 42
|
|
62
|
-
"PostfixUnaryExpression", // 43
|
|
63
|
-
"ConditionalExpression", // 44
|
|
64
|
-
"CallExpression", // 45
|
|
65
|
-
"PropertyAccessExpression", // 46
|
|
66
|
-
"ElementAccessExpression", // 47
|
|
67
|
-
"NewExpression", // 48
|
|
68
|
-
"ParenthesizedExpression", // 49
|
|
69
|
-
"TypeAssertionExpression", // 50
|
|
70
|
-
"AsExpression", // 51
|
|
71
|
-
"NonNullExpression", // 52
|
|
72
|
-
"TemplateExpression", // 53
|
|
73
|
-
"ArrayLiteralExpression", // 54
|
|
74
|
-
"ObjectLiteralExpression", // 55
|
|
75
|
-
"ExpressionStatement", // 56
|
|
76
|
-
];
|
|
77
|
-
// ============================================================================
|
|
78
|
-
// 15 Token Types (leaf node categories, indices 57-71)
|
|
79
|
-
// ============================================================================
|
|
80
|
-
export const TOKEN_TYPES = [
|
|
81
|
-
"Identifier", // 57
|
|
82
|
-
"StringLiteral", // 58
|
|
83
|
-
"NumericLiteral", // 59
|
|
84
|
-
"TrueKeyword", // 60
|
|
85
|
-
"FalseKeyword", // 61
|
|
86
|
-
"NullKeyword", // 62
|
|
87
|
-
"UndefinedKeyword", // 63
|
|
88
|
-
"ThisKeyword", // 64
|
|
89
|
-
"SuperKeyword", // 65
|
|
90
|
-
"RegularExpressionLiteral", // 66
|
|
91
|
-
"NoSubstitutionTemplateLiteral", // 67
|
|
92
|
-
"TemplateHead", // 68
|
|
93
|
-
"TemplateMiddle", // 69
|
|
94
|
-
"TemplateTail", // 70
|
|
95
|
-
"ComputedPropertyName", // 71
|
|
96
|
-
];
|
|
97
|
-
// ============================================================================
|
|
98
|
-
// Constants
|
|
99
|
-
// ============================================================================
|
|
100
|
-
export const NUM_SYNTAX = SYNTAX_TYPES.length; // 57
|
|
101
|
-
export const NUM_TOKEN = TOKEN_TYPES.length; // 15
|
|
102
|
-
export const NUM_STATES = NUM_SYNTAX + NUM_TOKEN; // 72
|
|
103
|
-
// ============================================================================
|
|
104
|
-
// State Index Mapping
|
|
105
|
-
// ============================================================================
|
|
106
|
-
/**
|
|
107
|
-
* Map a TypeScript AST node to its Amain state index (0-71)
|
|
108
|
-
*/
|
|
109
|
-
export function getStateIndex(node) {
|
|
110
|
-
// Try syntax types first (0-56)
|
|
111
|
-
const kindName = ts.SyntaxKind[node.kind];
|
|
112
|
-
const syntaxIdx = SYNTAX_TYPES.indexOf(kindName);
|
|
113
|
-
if (syntaxIdx !== -1)
|
|
114
|
-
return syntaxIdx;
|
|
115
|
-
// Map to token types (57-71) based on node kind
|
|
116
|
-
if (ts.isIdentifier(node))
|
|
117
|
-
return 57;
|
|
118
|
-
if (ts.isStringLiteral(node))
|
|
119
|
-
return 58;
|
|
120
|
-
if (ts.isNumericLiteral(node))
|
|
121
|
-
return 59;
|
|
122
|
-
if (node.kind === ts.SyntaxKind.TrueKeyword)
|
|
123
|
-
return 60;
|
|
124
|
-
if (node.kind === ts.SyntaxKind.FalseKeyword)
|
|
125
|
-
return 61;
|
|
126
|
-
if (node.kind === ts.SyntaxKind.NullKeyword)
|
|
127
|
-
return 62;
|
|
128
|
-
if (node.kind === ts.SyntaxKind.UndefinedKeyword)
|
|
129
|
-
return 63;
|
|
130
|
-
if (node.kind === ts.SyntaxKind.ThisKeyword)
|
|
131
|
-
return 64;
|
|
132
|
-
if (node.kind === ts.SyntaxKind.SuperKeyword)
|
|
133
|
-
return 65;
|
|
134
|
-
if (node.kind === ts.SyntaxKind.RegularExpressionLiteral)
|
|
135
|
-
return 66;
|
|
136
|
-
if (node.kind === ts.SyntaxKind.NoSubstitutionTemplateLiteral)
|
|
137
|
-
return 67;
|
|
138
|
-
if (node.kind === ts.SyntaxKind.TemplateHead)
|
|
139
|
-
return 68;
|
|
140
|
-
if (node.kind === ts.SyntaxKind.TemplateMiddle)
|
|
141
|
-
return 69;
|
|
142
|
-
if (node.kind === ts.SyntaxKind.TemplateTail)
|
|
143
|
-
return 70;
|
|
144
|
-
if (node.kind === ts.SyntaxKind.ComputedPropertyName)
|
|
145
|
-
return 71;
|
|
146
|
-
// Default: treat as identifier for any other leaf node
|
|
147
|
-
return 57;
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Check if a node is a syntax type (non-leaf) vs token type (leaf)
|
|
151
|
-
*/
|
|
152
|
-
export function isSyntaxNode(node) {
|
|
153
|
-
return getStateIndex(node) < NUM_SYNTAX;
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Get state name for debugging
|
|
157
|
-
*/
|
|
158
|
-
export function getStateName(index) {
|
|
159
|
-
if (index < NUM_SYNTAX)
|
|
160
|
-
return SYNTAX_TYPES[index];
|
|
161
|
-
if (index < NUM_STATES)
|
|
162
|
-
return TOKEN_TYPES[index - NUM_SYNTAX];
|
|
163
|
-
return "Unknown";
|
|
164
|
-
}
|