pi-rtk-optimizer 0.3.3 → 0.5.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/CHANGELOG.md +102 -67
- package/README.md +292 -290
- package/config/config.example.json +36 -35
- package/package.json +4 -4
- package/src/additional-coverage-test.ts +278 -0
- package/src/boolean-format.ts +3 -0
- package/src/command-rewriter-test.ts +160 -120
- package/src/command-rewriter.ts +594 -585
- package/src/config-modal-test.ts +168 -0
- package/src/config-modal.ts +613 -600
- package/src/config-store.ts +224 -217
- package/src/index-test.ts +54 -0
- package/src/index.ts +410 -289
- package/src/output-compactor-test.ts +500 -158
- package/src/output-compactor.ts +432 -349
- package/src/record-utils.ts +6 -0
- package/src/rewrite-bypass.ts +332 -173
- package/src/rewrite-pipeline-safety.ts +154 -0
- package/src/rewrite-rules.ts +255 -255
- package/src/rtk-command-environment.ts +64 -0
- package/src/runtime-guard-test.ts +42 -50
- package/src/runtime-guard.ts +14 -14
- package/src/techniques/build.ts +155 -155
- package/src/techniques/emoji.ts +91 -0
- package/src/techniques/git.ts +231 -229
- package/src/techniques/index.ts +10 -16
- package/src/techniques/linter.ts +151 -161
- package/src/techniques/path-utils.ts +67 -0
- package/src/techniques/rtk.ts +136 -0
- package/src/techniques/search.ts +67 -76
- package/src/techniques/source.ts +253 -253
- package/src/techniques/test-output.ts +172 -172
- package/src/test-helpers.ts +10 -0
- package/src/tool-execution-sanitizer.ts +69 -0
- package/src/types-shims.d.ts +192 -183
- package/src/types.ts +103 -114
- package/src/zellij-modal.ts +1001 -1001
- package/src/compat-commands.ts +0 -207
package/src/config-store.ts
CHANGED
|
@@ -1,217 +1,224 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { dirname } from "node:path";
|
|
3
|
-
import { CONFIG_PATH } from "./constants.js";
|
|
4
|
-
import {
|
|
5
|
-
DEFAULT_RTK_INTEGRATION_CONFIG,
|
|
6
|
-
RTK_MODES,
|
|
7
|
-
RTK_SOURCE_FILTER_LEVELS,
|
|
8
|
-
type ConfigLoadResult,
|
|
9
|
-
type ConfigSaveResult,
|
|
10
|
-
type EnsureConfigResult,
|
|
11
|
-
type RtkIntegrationConfig,
|
|
12
|
-
type RtkSourceFilterLevel,
|
|
13
|
-
} from "./types.js";
|
|
14
|
-
|
|
15
|
-
function toBoolean(value: unknown, fallback: boolean): boolean {
|
|
16
|
-
return typeof value === "boolean" ? value : fallback;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function toInteger(value: unknown, fallback: number, min: number, max: number): number {
|
|
20
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
21
|
-
return fallback;
|
|
22
|
-
}
|
|
23
|
-
const rounded = Math.round(value);
|
|
24
|
-
return Math.max(min, Math.min(max, rounded));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function toMode(value: unknown): RtkIntegrationConfig["mode"] {
|
|
28
|
-
return RTK_MODES.includes(value as RtkIntegrationConfig["mode"])
|
|
29
|
-
? (value as RtkIntegrationConfig["mode"])
|
|
30
|
-
: DEFAULT_RTK_INTEGRATION_CONFIG.mode;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function toSourceFilterLevel(value: unknown): RtkSourceFilterLevel {
|
|
34
|
-
return RTK_SOURCE_FILTER_LEVELS.includes(value as RtkSourceFilterLevel)
|
|
35
|
-
? (value as RtkSourceFilterLevel)
|
|
36
|
-
: DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFiltering;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function toObject(value: unknown): Record<string, unknown> {
|
|
40
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
41
|
-
return {};
|
|
42
|
-
}
|
|
43
|
-
return value as Record<string, unknown>;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfig {
|
|
47
|
-
const source = toObject(raw);
|
|
48
|
-
const outputCompactionSource = toObject(source.outputCompaction);
|
|
49
|
-
const truncateSource = toObject(outputCompactionSource.truncate);
|
|
50
|
-
const smartTruncateSource = toObject(outputCompactionSource.smartTruncate);
|
|
51
|
-
|
|
52
|
-
return {
|
|
53
|
-
enabled: toBoolean(source.enabled, DEFAULT_RTK_INTEGRATION_CONFIG.enabled),
|
|
54
|
-
mode: toMode(source.mode),
|
|
55
|
-
guardWhenRtkMissing: toBoolean(
|
|
56
|
-
source.guardWhenRtkMissing,
|
|
57
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.guardWhenRtkMissing,
|
|
58
|
-
),
|
|
59
|
-
showRewriteNotifications: toBoolean(
|
|
60
|
-
source.showRewriteNotifications,
|
|
61
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.showRewriteNotifications,
|
|
62
|
-
),
|
|
63
|
-
rewriteGitGithub: toBoolean(
|
|
64
|
-
source.rewriteGitGithub,
|
|
65
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGitGithub,
|
|
66
|
-
),
|
|
67
|
-
rewriteFilesystem: toBoolean(
|
|
68
|
-
source.rewriteFilesystem,
|
|
69
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteFilesystem,
|
|
70
|
-
),
|
|
71
|
-
rewriteRust: toBoolean(source.rewriteRust, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteRust),
|
|
72
|
-
rewriteJavaScript: toBoolean(
|
|
73
|
-
source.rewriteJavaScript,
|
|
74
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteJavaScript,
|
|
75
|
-
),
|
|
76
|
-
rewritePython: toBoolean(source.rewritePython, DEFAULT_RTK_INTEGRATION_CONFIG.rewritePython),
|
|
77
|
-
rewriteGo: toBoolean(source.rewriteGo, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGo),
|
|
78
|
-
rewriteContainers: toBoolean(
|
|
79
|
-
source.rewriteContainers,
|
|
80
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteContainers,
|
|
81
|
-
),
|
|
82
|
-
rewriteNetwork: toBoolean(source.rewriteNetwork, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteNetwork),
|
|
83
|
-
rewritePackageManagers: toBoolean(
|
|
84
|
-
source.rewritePackageManagers,
|
|
85
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.rewritePackageManagers,
|
|
86
|
-
),
|
|
87
|
-
outputCompaction: {
|
|
88
|
-
enabled: toBoolean(
|
|
89
|
-
outputCompactionSource.enabled,
|
|
90
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.enabled,
|
|
91
|
-
),
|
|
92
|
-
stripAnsi: toBoolean(
|
|
93
|
-
outputCompactionSource.stripAnsi,
|
|
94
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.stripAnsi,
|
|
95
|
-
),
|
|
96
|
-
sourceCodeFilteringEnabled: toBoolean(
|
|
97
|
-
outputCompactionSource.sourceCodeFilteringEnabled,
|
|
98
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFilteringEnabled,
|
|
99
|
-
),
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
truncateSource.
|
|
107
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.truncate.
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
smartTruncateSource.
|
|
120
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.smartTruncate.
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
outputCompactionSource.
|
|
131
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.
|
|
132
|
-
),
|
|
133
|
-
|
|
134
|
-
outputCompactionSource.
|
|
135
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.
|
|
136
|
-
),
|
|
137
|
-
|
|
138
|
-
outputCompactionSource.
|
|
139
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.
|
|
140
|
-
),
|
|
141
|
-
|
|
142
|
-
outputCompactionSource.
|
|
143
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.
|
|
144
|
-
),
|
|
145
|
-
|
|
146
|
-
outputCompactionSource.
|
|
147
|
-
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.
|
|
148
|
-
),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
const
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
}
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname } from "node:path";
|
|
3
|
+
import { CONFIG_PATH } from "./constants.js";
|
|
4
|
+
import {
|
|
5
|
+
DEFAULT_RTK_INTEGRATION_CONFIG,
|
|
6
|
+
RTK_MODES,
|
|
7
|
+
RTK_SOURCE_FILTER_LEVELS,
|
|
8
|
+
type ConfigLoadResult,
|
|
9
|
+
type ConfigSaveResult,
|
|
10
|
+
type EnsureConfigResult,
|
|
11
|
+
type RtkIntegrationConfig,
|
|
12
|
+
type RtkSourceFilterLevel,
|
|
13
|
+
} from "./types.js";
|
|
14
|
+
|
|
15
|
+
function toBoolean(value: unknown, fallback: boolean): boolean {
|
|
16
|
+
return typeof value === "boolean" ? value : fallback;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function toInteger(value: unknown, fallback: number, min: number, max: number): number {
|
|
20
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
21
|
+
return fallback;
|
|
22
|
+
}
|
|
23
|
+
const rounded = Math.round(value);
|
|
24
|
+
return Math.max(min, Math.min(max, rounded));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function toMode(value: unknown): RtkIntegrationConfig["mode"] {
|
|
28
|
+
return RTK_MODES.includes(value as RtkIntegrationConfig["mode"])
|
|
29
|
+
? (value as RtkIntegrationConfig["mode"])
|
|
30
|
+
: DEFAULT_RTK_INTEGRATION_CONFIG.mode;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function toSourceFilterLevel(value: unknown): RtkSourceFilterLevel {
|
|
34
|
+
return RTK_SOURCE_FILTER_LEVELS.includes(value as RtkSourceFilterLevel)
|
|
35
|
+
? (value as RtkSourceFilterLevel)
|
|
36
|
+
: DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFiltering;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function toObject(value: unknown): Record<string, unknown> {
|
|
40
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
41
|
+
return {};
|
|
42
|
+
}
|
|
43
|
+
return value as Record<string, unknown>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function normalizeRtkIntegrationConfig(raw: unknown): RtkIntegrationConfig {
|
|
47
|
+
const source = toObject(raw);
|
|
48
|
+
const outputCompactionSource = toObject(source.outputCompaction);
|
|
49
|
+
const truncateSource = toObject(outputCompactionSource.truncate);
|
|
50
|
+
const smartTruncateSource = toObject(outputCompactionSource.smartTruncate);
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
enabled: toBoolean(source.enabled, DEFAULT_RTK_INTEGRATION_CONFIG.enabled),
|
|
54
|
+
mode: toMode(source.mode),
|
|
55
|
+
guardWhenRtkMissing: toBoolean(
|
|
56
|
+
source.guardWhenRtkMissing,
|
|
57
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.guardWhenRtkMissing,
|
|
58
|
+
),
|
|
59
|
+
showRewriteNotifications: toBoolean(
|
|
60
|
+
source.showRewriteNotifications,
|
|
61
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.showRewriteNotifications,
|
|
62
|
+
),
|
|
63
|
+
rewriteGitGithub: toBoolean(
|
|
64
|
+
source.rewriteGitGithub,
|
|
65
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGitGithub,
|
|
66
|
+
),
|
|
67
|
+
rewriteFilesystem: toBoolean(
|
|
68
|
+
source.rewriteFilesystem,
|
|
69
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteFilesystem,
|
|
70
|
+
),
|
|
71
|
+
rewriteRust: toBoolean(source.rewriteRust, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteRust),
|
|
72
|
+
rewriteJavaScript: toBoolean(
|
|
73
|
+
source.rewriteJavaScript,
|
|
74
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteJavaScript,
|
|
75
|
+
),
|
|
76
|
+
rewritePython: toBoolean(source.rewritePython, DEFAULT_RTK_INTEGRATION_CONFIG.rewritePython),
|
|
77
|
+
rewriteGo: toBoolean(source.rewriteGo, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteGo),
|
|
78
|
+
rewriteContainers: toBoolean(
|
|
79
|
+
source.rewriteContainers,
|
|
80
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.rewriteContainers,
|
|
81
|
+
),
|
|
82
|
+
rewriteNetwork: toBoolean(source.rewriteNetwork, DEFAULT_RTK_INTEGRATION_CONFIG.rewriteNetwork),
|
|
83
|
+
rewritePackageManagers: toBoolean(
|
|
84
|
+
source.rewritePackageManagers,
|
|
85
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.rewritePackageManagers,
|
|
86
|
+
),
|
|
87
|
+
outputCompaction: {
|
|
88
|
+
enabled: toBoolean(
|
|
89
|
+
outputCompactionSource.enabled,
|
|
90
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.enabled,
|
|
91
|
+
),
|
|
92
|
+
stripAnsi: toBoolean(
|
|
93
|
+
outputCompactionSource.stripAnsi,
|
|
94
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.stripAnsi,
|
|
95
|
+
),
|
|
96
|
+
sourceCodeFilteringEnabled: toBoolean(
|
|
97
|
+
outputCompactionSource.sourceCodeFilteringEnabled,
|
|
98
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.sourceCodeFilteringEnabled,
|
|
99
|
+
),
|
|
100
|
+
preserveExactSkillReads: toBoolean(
|
|
101
|
+
outputCompactionSource.preserveExactSkillReads,
|
|
102
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.preserveExactSkillReads,
|
|
103
|
+
),
|
|
104
|
+
truncate: {
|
|
105
|
+
enabled: toBoolean(
|
|
106
|
+
truncateSource.enabled,
|
|
107
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.truncate.enabled,
|
|
108
|
+
),
|
|
109
|
+
maxChars: toInteger(
|
|
110
|
+
truncateSource.maxChars,
|
|
111
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.truncate.maxChars,
|
|
112
|
+
1_000,
|
|
113
|
+
200_000,
|
|
114
|
+
),
|
|
115
|
+
},
|
|
116
|
+
sourceCodeFiltering: toSourceFilterLevel(outputCompactionSource.sourceCodeFiltering),
|
|
117
|
+
smartTruncate: {
|
|
118
|
+
enabled: toBoolean(
|
|
119
|
+
smartTruncateSource.enabled,
|
|
120
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.smartTruncate.enabled,
|
|
121
|
+
),
|
|
122
|
+
maxLines: toInteger(
|
|
123
|
+
smartTruncateSource.maxLines,
|
|
124
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.smartTruncate.maxLines,
|
|
125
|
+
40,
|
|
126
|
+
4_000,
|
|
127
|
+
),
|
|
128
|
+
},
|
|
129
|
+
aggregateTestOutput: toBoolean(
|
|
130
|
+
outputCompactionSource.aggregateTestOutput,
|
|
131
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.aggregateTestOutput,
|
|
132
|
+
),
|
|
133
|
+
filterBuildOutput: toBoolean(
|
|
134
|
+
outputCompactionSource.filterBuildOutput,
|
|
135
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.filterBuildOutput,
|
|
136
|
+
),
|
|
137
|
+
compactGitOutput: toBoolean(
|
|
138
|
+
outputCompactionSource.compactGitOutput,
|
|
139
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.compactGitOutput,
|
|
140
|
+
),
|
|
141
|
+
aggregateLinterOutput: toBoolean(
|
|
142
|
+
outputCompactionSource.aggregateLinterOutput,
|
|
143
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.aggregateLinterOutput,
|
|
144
|
+
),
|
|
145
|
+
groupSearchOutput: toBoolean(
|
|
146
|
+
outputCompactionSource.groupSearchOutput,
|
|
147
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.groupSearchOutput,
|
|
148
|
+
),
|
|
149
|
+
trackSavings: toBoolean(
|
|
150
|
+
outputCompactionSource.trackSavings,
|
|
151
|
+
DEFAULT_RTK_INTEGRATION_CONFIG.outputCompaction.trackSavings,
|
|
152
|
+
),
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function ensureConfigExists(configPath = CONFIG_PATH): EnsureConfigResult {
|
|
158
|
+
if (existsSync(configPath)) {
|
|
159
|
+
return { created: false };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
try {
|
|
163
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
164
|
+
writeFileSync(configPath, `${JSON.stringify(DEFAULT_RTK_INTEGRATION_CONFIG, null, 2)}\n`, "utf-8");
|
|
165
|
+
return { created: true };
|
|
166
|
+
} catch (error) {
|
|
167
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
168
|
+
return {
|
|
169
|
+
created: false,
|
|
170
|
+
error: `Failed to create ${configPath}: ${message}`,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export function loadRtkIntegrationConfig(configPath = CONFIG_PATH): ConfigLoadResult {
|
|
176
|
+
if (!existsSync(configPath)) {
|
|
177
|
+
return { config: { ...DEFAULT_RTK_INTEGRATION_CONFIG } };
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
const rawText = readFileSync(configPath, "utf-8");
|
|
182
|
+
const parsed = JSON.parse(rawText) as unknown;
|
|
183
|
+
return { config: normalizeRtkIntegrationConfig(parsed) };
|
|
184
|
+
} catch (error) {
|
|
185
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
186
|
+
return {
|
|
187
|
+
config: { ...DEFAULT_RTK_INTEGRATION_CONFIG },
|
|
188
|
+
warning: `Failed to parse ${configPath}: ${message}`,
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function saveRtkIntegrationConfig(
|
|
194
|
+
config: RtkIntegrationConfig,
|
|
195
|
+
configPath = CONFIG_PATH,
|
|
196
|
+
): ConfigSaveResult {
|
|
197
|
+
const normalized = normalizeRtkIntegrationConfig(config);
|
|
198
|
+
const tmpPath = `${configPath}.tmp`;
|
|
199
|
+
|
|
200
|
+
try {
|
|
201
|
+
mkdirSync(dirname(configPath), { recursive: true });
|
|
202
|
+
writeFileSync(tmpPath, `${JSON.stringify(normalized, null, 2)}\n`, "utf-8");
|
|
203
|
+
renameSync(tmpPath, configPath);
|
|
204
|
+
return { success: true };
|
|
205
|
+
} catch (error) {
|
|
206
|
+
try {
|
|
207
|
+
if (existsSync(tmpPath)) {
|
|
208
|
+
unlinkSync(tmpPath);
|
|
209
|
+
}
|
|
210
|
+
} catch {
|
|
211
|
+
// Ignore cleanup failures.
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
error: `Failed to save ${configPath}: ${message}`,
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export function getRtkIntegrationConfigPath(configPath = CONFIG_PATH): string {
|
|
223
|
+
return configPath;
|
|
224
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import { mock } from "bun:test";
|
|
3
|
+
|
|
4
|
+
import { runTest } from "./test-helpers.ts";
|
|
5
|
+
|
|
6
|
+
mock.module("@mariozechner/pi-coding-agent", () => ({
|
|
7
|
+
getSettingsListTheme: () => ({}),
|
|
8
|
+
isToolCallEventType: (toolName: string, event: Record<string, unknown>) => event.toolName === toolName,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
mock.module("@mariozechner/pi-tui", () => ({
|
|
12
|
+
Box: class {},
|
|
13
|
+
Container: class {
|
|
14
|
+
addChild(): void {}
|
|
15
|
+
render(): string[] {
|
|
16
|
+
return [];
|
|
17
|
+
}
|
|
18
|
+
invalidate(): void {}
|
|
19
|
+
},
|
|
20
|
+
SettingsList: class {
|
|
21
|
+
handleInput(): void {}
|
|
22
|
+
updateValue(): void {}
|
|
23
|
+
},
|
|
24
|
+
Spacer: class {},
|
|
25
|
+
Text: class {},
|
|
26
|
+
truncateToWidth: (text: string) => text,
|
|
27
|
+
visibleWidth: (text: string) => text.length,
|
|
28
|
+
}));
|
|
29
|
+
|
|
30
|
+
const { createBoundedNoticeTracker } = await import("./index.ts");
|
|
31
|
+
|
|
32
|
+
runTest("bounded notice tracker evicts old entries and supports reset", () => {
|
|
33
|
+
const tracker = createBoundedNoticeTracker(2);
|
|
34
|
+
|
|
35
|
+
assert.equal(tracker.remember("first"), true);
|
|
36
|
+
assert.equal(tracker.remember("second"), true);
|
|
37
|
+
assert.equal(tracker.remember("first"), false);
|
|
38
|
+
|
|
39
|
+
assert.equal(tracker.remember("third"), true);
|
|
40
|
+
assert.equal(tracker.remember("second"), false);
|
|
41
|
+
assert.equal(tracker.remember("first"), true);
|
|
42
|
+
|
|
43
|
+
tracker.reset();
|
|
44
|
+
assert.equal(tracker.remember("third"), true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
runTest("bounded notice tracker coerces invalid limits to a safe minimum", () => {
|
|
48
|
+
const tracker = createBoundedNoticeTracker(0);
|
|
49
|
+
assert.equal(tracker.remember("alpha"), true);
|
|
50
|
+
assert.equal(tracker.remember("beta"), true);
|
|
51
|
+
assert.equal(tracker.remember("alpha"), true);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
console.log("All index tests passed.");
|