@ryuenn3123/agentic-senior-core 2.0.4 → 2.0.5
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/.agent-context/blueprints/mobile-app.md +21 -21
- package/.agent-context/profiles/platform.md +13 -13
- package/.agent-context/profiles/regulated.md +13 -13
- package/.agent-context/profiles/startup.md +13 -13
- package/.agent-context/review-checklists/frontend-skill-parity.md +28 -28
- package/.agent-context/review-checklists/frontend-usability.md +33 -33
- package/.agent-context/review-checklists/release-operations.md +29 -29
- package/.agent-context/skills/README.md +62 -62
- package/.agent-context/skills/backend/README.md +67 -67
- package/.agent-context/skills/backend/architecture.md +360 -360
- package/.agent-context/skills/backend/compatibility-manifest.json +8 -8
- package/.agent-context/skills/backend/data-access.md +230 -230
- package/.agent-context/skills/backend/errors.md +137 -137
- package/.agent-context/skills/backend/validation.md +116 -116
- package/.agent-context/skills/backend.md +28 -28
- package/.agent-context/skills/cli/README.md +49 -49
- package/.agent-context/skills/cli/compatibility-manifest.json +8 -8
- package/.agent-context/skills/cli/init.md +37 -37
- package/.agent-context/skills/cli/output.md +35 -35
- package/.agent-context/skills/cli/upgrade.md +37 -37
- package/.agent-context/skills/cli.md +28 -28
- package/.agent-context/skills/distribution/README.md +18 -18
- package/.agent-context/skills/distribution/compatibility-manifest.json +8 -8
- package/.agent-context/skills/distribution/compatibility.md +31 -31
- package/.agent-context/skills/distribution/publish.md +36 -36
- package/.agent-context/skills/distribution/rollback.md +31 -31
- package/.agent-context/skills/distribution.md +28 -28
- package/.agent-context/skills/frontend/README.md +35 -35
- package/.agent-context/skills/frontend/accessibility.md +107 -107
- package/.agent-context/skills/frontend/compatibility-manifest.json +8 -8
- package/.agent-context/skills/frontend/motion.md +66 -66
- package/.agent-context/skills/frontend/performance.md +62 -62
- package/.agent-context/skills/frontend/ui-architecture.md +128 -128
- package/.agent-context/skills/frontend.md +29 -29
- package/.agent-context/skills/fullstack/README.md +18 -18
- package/.agent-context/skills/fullstack/compatibility-manifest.json +8 -8
- package/.agent-context/skills/fullstack/contracts.md +52 -52
- package/.agent-context/skills/fullstack/end-to-end.md +41 -41
- package/.agent-context/skills/fullstack/feature-slicing.md +64 -64
- package/.agent-context/skills/fullstack.md +26 -26
- package/.agent-context/skills/index.json +107 -107
- package/.agent-context/skills/review-quality/README.md +18 -18
- package/.agent-context/skills/review-quality/benchmark.md +29 -29
- package/.agent-context/skills/review-quality/compatibility-manifest.json +8 -8
- package/.agent-context/skills/review-quality/planning.md +37 -37
- package/.agent-context/skills/review-quality/security.md +33 -33
- package/.agent-context/skills/review-quality.md +27 -27
- package/.agent-context/stacks/flutter.md +16 -16
- package/.agent-context/stacks/react-native.md +16 -16
- package/.agent-context/state/architecture-map.md +25 -25
- package/.agent-context/state/benchmark-analysis.json +431 -431
- package/.agent-context/state/benchmark-thresholds.json +10 -10
- package/.agent-context/state/benchmark-watchlist.json +19 -19
- package/.agent-context/state/dependency-map.md +32 -32
- package/.agent-context/state/quality-trend-report.json +79 -0
- package/.agent-context/state/skill-platform.json +38 -38
- package/.agent-override.md +36 -36
- package/.cursorrules +1 -1
- package/.gemini/instructions.md +21 -97
- package/.github/ISSUE_TEMPLATE/v1.7-frontend-work-item.yml +54 -54
- package/.github/copilot-instructions.md +21 -166
- package/.github/workflows/benchmark-detection.yml +38 -38
- package/.github/workflows/benchmark-intelligence.yml +50 -50
- package/.github/workflows/frontend-usability-gate.yml +36 -36
- package/.github/workflows/release-gate.yml +32 -32
- package/.github/workflows/sbom-compliance.yml +32 -32
- package/.windsurfrules +1 -1
- package/AGENTS.md +28 -181
- package/README.md +368 -368
- package/lib/cli/commands/optimize.mjs +171 -171
- package/lib/cli/compatibility.mjs +124 -124
- package/lib/cli/token-optimization.mjs +275 -275
- package/mcp.json +92 -92
- package/package.json +2 -1
- package/scripts/benchmark-gate.mjs +121 -121
- package/scripts/benchmark-intelligence.mjs +140 -140
- package/scripts/detection-benchmark.mjs +138 -138
- package/scripts/frontend-usability-audit.mjs +87 -87
- package/scripts/generate-sbom.mjs +61 -61
- package/scripts/init-project.ps1 +104 -104
- package/scripts/llm-judge.mjs +664 -664
- package/scripts/quality-trend-report.mjs +289 -0
- package/scripts/release-gate.mjs +259 -204
- package/scripts/skill-tier-policy.mjs +75 -75
- package/scripts/token-optimization-benchmark.mjs +252 -252
- package/scripts/validate.mjs +865 -811
|
@@ -1,275 +1,275 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { spawnSync } from 'node:child_process';
|
|
4
|
-
import { platform } from 'node:process';
|
|
5
|
-
|
|
6
|
-
import { pathExists } from './utils.mjs';
|
|
7
|
-
|
|
8
|
-
export const TOKEN_OPTIMIZATION_STATE_FILE_NAME = 'token-optimization.json';
|
|
9
|
-
export const TOKEN_OPTIMIZATION_REPORT_FILE_NAME = 'token-optimization-report.json';
|
|
10
|
-
|
|
11
|
-
const TOKEN_OPTIMIZATION_SCHEMA_VERSION = 'token-optimization-v1';
|
|
12
|
-
const FALLBACK_AGENT_NAME = 'copilot';
|
|
13
|
-
|
|
14
|
-
const SUPPORTED_AGENT_NAMES = new Set([
|
|
15
|
-
'copilot',
|
|
16
|
-
'claude',
|
|
17
|
-
'cursor',
|
|
18
|
-
'windsurf',
|
|
19
|
-
'gemini',
|
|
20
|
-
'codex',
|
|
21
|
-
'cline',
|
|
22
|
-
]);
|
|
23
|
-
|
|
24
|
-
const COMMAND_REWRITE_MAPPINGS = [
|
|
25
|
-
{
|
|
26
|
-
rawCommand: 'ls -la',
|
|
27
|
-
optimizedCommand: 'rtk ls .',
|
|
28
|
-
reason: 'Directory listings are grouped and compacted before entering the model context.',
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
rawCommand: 'tree',
|
|
32
|
-
optimizedCommand: 'rtk ls .',
|
|
33
|
-
reason: 'Large trees are summarized without losing high-signal structure.',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
rawCommand: 'cat file.ext',
|
|
37
|
-
optimizedCommand: 'rtk read file.ext',
|
|
38
|
-
reason: 'File reads keep signatures and relevant lines while trimming boilerplate.',
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
rawCommand: 'rg pattern .',
|
|
42
|
-
optimizedCommand: 'rtk grep "pattern" .',
|
|
43
|
-
reason: 'Search hits are grouped and deduplicated for faster reasoning.',
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
rawCommand: 'git status',
|
|
47
|
-
optimizedCommand: 'rtk git status',
|
|
48
|
-
reason: 'Status output is condensed to actionable file changes.',
|
|
49
|
-
},
|
|
50
|
-
{
|
|
51
|
-
rawCommand: 'git diff',
|
|
52
|
-
optimizedCommand: 'rtk git diff',
|
|
53
|
-
reason: 'Diff noise is trimmed while preserving code-review context.',
|
|
54
|
-
},
|
|
55
|
-
{
|
|
56
|
-
rawCommand: 'git log -n 10',
|
|
57
|
-
optimizedCommand: 'rtk git log -n 10',
|
|
58
|
-
reason: 'History is converted to compact one-line summaries.',
|
|
59
|
-
},
|
|
60
|
-
{
|
|
61
|
-
rawCommand: 'npm test',
|
|
62
|
-
optimizedCommand: 'rtk test npm test',
|
|
63
|
-
reason: 'Test results emphasize failures and suppress repetitive pass logs.',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
rawCommand: 'npm run build',
|
|
67
|
-
optimizedCommand: 'rtk err npm run build',
|
|
68
|
-
reason: 'Build runs return errors and warnings only for quicker remediation.',
|
|
69
|
-
},
|
|
70
|
-
{
|
|
71
|
-
rawCommand: 'tsc --noEmit',
|
|
72
|
-
optimizedCommand: 'rtk tsc',
|
|
73
|
-
reason: 'TypeScript diagnostics are grouped by file with less repetition.',
|
|
74
|
-
},
|
|
75
|
-
{
|
|
76
|
-
rawCommand: 'eslint .',
|
|
77
|
-
optimizedCommand: 'rtk lint',
|
|
78
|
-
reason: 'Lint output is grouped by rule and location to reduce token churn.',
|
|
79
|
-
},
|
|
80
|
-
{
|
|
81
|
-
rawCommand: 'docker ps',
|
|
82
|
-
optimizedCommand: 'rtk docker ps',
|
|
83
|
-
reason: 'Container state is summarized into compact rows.',
|
|
84
|
-
},
|
|
85
|
-
{
|
|
86
|
-
rawCommand: 'kubectl get pods',
|
|
87
|
-
optimizedCommand: 'rtk kubectl pods',
|
|
88
|
-
reason: 'Kubernetes output is condensed and stable for agent consumption.',
|
|
89
|
-
},
|
|
90
|
-
];
|
|
91
|
-
|
|
92
|
-
function parseRtkVersion(versionOutput) {
|
|
93
|
-
const versionMatch = versionOutput.match(/\d+\.\d+\.\d+/);
|
|
94
|
-
return versionMatch ? versionMatch[0] : null;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function normalizeAgentName(rawAgentName = FALLBACK_AGENT_NAME) {
|
|
98
|
-
const normalizedAgentName = String(rawAgentName || '')
|
|
99
|
-
.trim()
|
|
100
|
-
.toLowerCase();
|
|
101
|
-
|
|
102
|
-
if (!normalizedAgentName) {
|
|
103
|
-
return FALLBACK_AGENT_NAME;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
if (!SUPPORTED_AGENT_NAMES.has(normalizedAgentName)) {
|
|
107
|
-
throw new Error(
|
|
108
|
-
`Unsupported agent "${rawAgentName}". Supported values: ${Array.from(SUPPORTED_AGENT_NAMES).join(', ')}`
|
|
109
|
-
);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return normalizedAgentName;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export function detectRtkBinary() {
|
|
116
|
-
try {
|
|
117
|
-
const versionResult = spawnSync('rtk', ['--version'], {
|
|
118
|
-
encoding: 'utf8',
|
|
119
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
if (versionResult.status !== 0) {
|
|
123
|
-
return {
|
|
124
|
-
isAvailable: false,
|
|
125
|
-
version: null,
|
|
126
|
-
detectionError: (versionResult.stderr || versionResult.stdout || 'Unknown external proxy detection error').trim(),
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
const detectedVersion = parseRtkVersion(versionResult.stdout || '');
|
|
131
|
-
return {
|
|
132
|
-
isAvailable: true,
|
|
133
|
-
version: detectedVersion,
|
|
134
|
-
detectionError: null,
|
|
135
|
-
};
|
|
136
|
-
} catch (detectionError) {
|
|
137
|
-
return {
|
|
138
|
-
isAvailable: false,
|
|
139
|
-
version: null,
|
|
140
|
-
detectionError: detectionError instanceof Error ? detectionError.message : String(detectionError),
|
|
141
|
-
};
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
export function buildRtkInstallHint() {
|
|
146
|
-
if (platform === 'win32') {
|
|
147
|
-
return 'Install the external token optimizer binary for Windows, extract it, and ensure the executable is on PATH.';
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
if (platform === 'darwin') {
|
|
151
|
-
return 'Install the external token optimizer with Homebrew, then verify the executable is available in your shell.';
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return 'Install the external token optimizer with the vendor installer, then verify it is available in your shell PATH.';
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export function buildRtkHookCommand(selectedAgentName) {
|
|
158
|
-
const normalizedAgentName = normalizeAgentName(selectedAgentName);
|
|
159
|
-
|
|
160
|
-
if (normalizedAgentName === 'copilot') {
|
|
161
|
-
return 'rtk init -g --copilot';
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (normalizedAgentName === 'claude') {
|
|
165
|
-
return 'rtk init -g';
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (normalizedAgentName === 'cursor') {
|
|
169
|
-
return 'rtk init -g --agent cursor';
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
if (normalizedAgentName === 'windsurf') {
|
|
173
|
-
return 'rtk init --agent windsurf';
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
if (normalizedAgentName === 'gemini') {
|
|
177
|
-
return 'rtk init -g --gemini';
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
if (normalizedAgentName === 'codex') {
|
|
181
|
-
return 'rtk init -g --codex';
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (normalizedAgentName === 'cline') {
|
|
185
|
-
return 'rtk init --agent cline';
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return 'rtk init -g --copilot';
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
export function createTokenOptimizationState({
|
|
192
|
-
isEnabled,
|
|
193
|
-
selectedAgentName,
|
|
194
|
-
rtkDetection,
|
|
195
|
-
}) {
|
|
196
|
-
return {
|
|
197
|
-
schemaVersion: TOKEN_OPTIMIZATION_SCHEMA_VERSION,
|
|
198
|
-
enabled: Boolean(isEnabled),
|
|
199
|
-
selectedAgent: normalizeAgentName(selectedAgentName),
|
|
200
|
-
preferredShellProxy: rtkDetection.isAvailable ? 'external-proxy' : 'native-fallback',
|
|
201
|
-
externalProxy: {
|
|
202
|
-
detected: Boolean(rtkDetection.isAvailable),
|
|
203
|
-
version: rtkDetection.version,
|
|
204
|
-
detectionError: rtkDetection.detectionError,
|
|
205
|
-
},
|
|
206
|
-
commandRewriteMappings: COMMAND_REWRITE_MAPPINGS.map((mapping) => ({ ...mapping })),
|
|
207
|
-
generatedAt: new Date().toISOString(),
|
|
208
|
-
};
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
export async function readTokenOptimizationState(targetDirectoryPath) {
|
|
212
|
-
const stateFilePath = path.join(
|
|
213
|
-
targetDirectoryPath,
|
|
214
|
-
'.agent-context',
|
|
215
|
-
'state',
|
|
216
|
-
TOKEN_OPTIMIZATION_STATE_FILE_NAME
|
|
217
|
-
);
|
|
218
|
-
|
|
219
|
-
if (!(await pathExists(stateFilePath))) {
|
|
220
|
-
return null;
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
try {
|
|
224
|
-
const stateContent = await fs.readFile(stateFilePath, 'utf8');
|
|
225
|
-
const parsedState = JSON.parse(stateContent);
|
|
226
|
-
if (typeof parsedState.enabled !== 'boolean') {
|
|
227
|
-
return null;
|
|
228
|
-
}
|
|
229
|
-
return parsedState;
|
|
230
|
-
} catch {
|
|
231
|
-
return null;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
export async function writeTokenOptimizationState(targetDirectoryPath, tokenOptimizationState) {
|
|
236
|
-
const stateDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'state');
|
|
237
|
-
const stateFilePath = path.join(stateDirectoryPath, TOKEN_OPTIMIZATION_STATE_FILE_NAME);
|
|
238
|
-
|
|
239
|
-
await fs.mkdir(stateDirectoryPath, { recursive: true });
|
|
240
|
-
await fs.writeFile(stateFilePath, JSON.stringify(tokenOptimizationState, null, 2) + '\n', 'utf8');
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
export function buildTokenOptimizationGuidanceBlock(tokenOptimizationState) {
|
|
244
|
-
if (!tokenOptimizationState?.enabled) {
|
|
245
|
-
return [
|
|
246
|
-
'Token optimization mode is disabled for this repository.',
|
|
247
|
-
'Use native shell commands with manual output limiting when needed.',
|
|
248
|
-
].join('\n');
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
const rewriteLines = (tokenOptimizationState.commandRewriteMappings || []).map(
|
|
252
|
-
(mapping) => `- ${mapping.rawCommand} => ${mapping.optimizedCommand} (${mapping.reason})`
|
|
253
|
-
);
|
|
254
|
-
|
|
255
|
-
const fallbackGuidance = [
|
|
256
|
-
'- Prefer command variants with bounded output (for example: git diff --stat, rg --max-count, and npm test -- --reporter=dot).',
|
|
257
|
-
'- Request only the lines or sections required for the current decision.',
|
|
258
|
-
'- If shell output is still large, summarize and continue iteratively instead of dumping full logs.',
|
|
259
|
-
];
|
|
260
|
-
|
|
261
|
-
return [
|
|
262
|
-
`Token optimization mode is enabled for agent: ${tokenOptimizationState.selectedAgent}.`,
|
|
263
|
-
`Preferred shell proxy: ${tokenOptimizationState.preferredShellProxy}.`,
|
|
264
|
-
'',
|
|
265
|
-
'Apply command rewrites before running verbose shell commands:',
|
|
266
|
-
...rewriteLines,
|
|
267
|
-
'',
|
|
268
|
-
'Important scope note:',
|
|
269
|
-
'- Shell rewrite hooks affect shell tool calls only.',
|
|
270
|
-
'- Built-in read/grep/glob style tools may bypass shell rewrites, so explicit compact shell commands should be preferred in high-volume sessions.',
|
|
271
|
-
'',
|
|
272
|
-
'Fallback behavior when external proxy is unavailable:',
|
|
273
|
-
...fallbackGuidance,
|
|
274
|
-
].join('\n');
|
|
275
|
-
}
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { spawnSync } from 'node:child_process';
|
|
4
|
+
import { platform } from 'node:process';
|
|
5
|
+
|
|
6
|
+
import { pathExists } from './utils.mjs';
|
|
7
|
+
|
|
8
|
+
export const TOKEN_OPTIMIZATION_STATE_FILE_NAME = 'token-optimization.json';
|
|
9
|
+
export const TOKEN_OPTIMIZATION_REPORT_FILE_NAME = 'token-optimization-report.json';
|
|
10
|
+
|
|
11
|
+
const TOKEN_OPTIMIZATION_SCHEMA_VERSION = 'token-optimization-v1';
|
|
12
|
+
const FALLBACK_AGENT_NAME = 'copilot';
|
|
13
|
+
|
|
14
|
+
const SUPPORTED_AGENT_NAMES = new Set([
|
|
15
|
+
'copilot',
|
|
16
|
+
'claude',
|
|
17
|
+
'cursor',
|
|
18
|
+
'windsurf',
|
|
19
|
+
'gemini',
|
|
20
|
+
'codex',
|
|
21
|
+
'cline',
|
|
22
|
+
]);
|
|
23
|
+
|
|
24
|
+
const COMMAND_REWRITE_MAPPINGS = [
|
|
25
|
+
{
|
|
26
|
+
rawCommand: 'ls -la',
|
|
27
|
+
optimizedCommand: 'rtk ls .',
|
|
28
|
+
reason: 'Directory listings are grouped and compacted before entering the model context.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
rawCommand: 'tree',
|
|
32
|
+
optimizedCommand: 'rtk ls .',
|
|
33
|
+
reason: 'Large trees are summarized without losing high-signal structure.',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
rawCommand: 'cat file.ext',
|
|
37
|
+
optimizedCommand: 'rtk read file.ext',
|
|
38
|
+
reason: 'File reads keep signatures and relevant lines while trimming boilerplate.',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
rawCommand: 'rg pattern .',
|
|
42
|
+
optimizedCommand: 'rtk grep "pattern" .',
|
|
43
|
+
reason: 'Search hits are grouped and deduplicated for faster reasoning.',
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
rawCommand: 'git status',
|
|
47
|
+
optimizedCommand: 'rtk git status',
|
|
48
|
+
reason: 'Status output is condensed to actionable file changes.',
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
rawCommand: 'git diff',
|
|
52
|
+
optimizedCommand: 'rtk git diff',
|
|
53
|
+
reason: 'Diff noise is trimmed while preserving code-review context.',
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
rawCommand: 'git log -n 10',
|
|
57
|
+
optimizedCommand: 'rtk git log -n 10',
|
|
58
|
+
reason: 'History is converted to compact one-line summaries.',
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
rawCommand: 'npm test',
|
|
62
|
+
optimizedCommand: 'rtk test npm test',
|
|
63
|
+
reason: 'Test results emphasize failures and suppress repetitive pass logs.',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
rawCommand: 'npm run build',
|
|
67
|
+
optimizedCommand: 'rtk err npm run build',
|
|
68
|
+
reason: 'Build runs return errors and warnings only for quicker remediation.',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
rawCommand: 'tsc --noEmit',
|
|
72
|
+
optimizedCommand: 'rtk tsc',
|
|
73
|
+
reason: 'TypeScript diagnostics are grouped by file with less repetition.',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
rawCommand: 'eslint .',
|
|
77
|
+
optimizedCommand: 'rtk lint',
|
|
78
|
+
reason: 'Lint output is grouped by rule and location to reduce token churn.',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
rawCommand: 'docker ps',
|
|
82
|
+
optimizedCommand: 'rtk docker ps',
|
|
83
|
+
reason: 'Container state is summarized into compact rows.',
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
rawCommand: 'kubectl get pods',
|
|
87
|
+
optimizedCommand: 'rtk kubectl pods',
|
|
88
|
+
reason: 'Kubernetes output is condensed and stable for agent consumption.',
|
|
89
|
+
},
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
function parseRtkVersion(versionOutput) {
|
|
93
|
+
const versionMatch = versionOutput.match(/\d+\.\d+\.\d+/);
|
|
94
|
+
return versionMatch ? versionMatch[0] : null;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function normalizeAgentName(rawAgentName = FALLBACK_AGENT_NAME) {
|
|
98
|
+
const normalizedAgentName = String(rawAgentName || '')
|
|
99
|
+
.trim()
|
|
100
|
+
.toLowerCase();
|
|
101
|
+
|
|
102
|
+
if (!normalizedAgentName) {
|
|
103
|
+
return FALLBACK_AGENT_NAME;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!SUPPORTED_AGENT_NAMES.has(normalizedAgentName)) {
|
|
107
|
+
throw new Error(
|
|
108
|
+
`Unsupported agent "${rawAgentName}". Supported values: ${Array.from(SUPPORTED_AGENT_NAMES).join(', ')}`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return normalizedAgentName;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function detectRtkBinary() {
|
|
116
|
+
try {
|
|
117
|
+
const versionResult = spawnSync('rtk', ['--version'], {
|
|
118
|
+
encoding: 'utf8',
|
|
119
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
if (versionResult.status !== 0) {
|
|
123
|
+
return {
|
|
124
|
+
isAvailable: false,
|
|
125
|
+
version: null,
|
|
126
|
+
detectionError: (versionResult.stderr || versionResult.stdout || 'Unknown external proxy detection error').trim(),
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const detectedVersion = parseRtkVersion(versionResult.stdout || '');
|
|
131
|
+
return {
|
|
132
|
+
isAvailable: true,
|
|
133
|
+
version: detectedVersion,
|
|
134
|
+
detectionError: null,
|
|
135
|
+
};
|
|
136
|
+
} catch (detectionError) {
|
|
137
|
+
return {
|
|
138
|
+
isAvailable: false,
|
|
139
|
+
version: null,
|
|
140
|
+
detectionError: detectionError instanceof Error ? detectionError.message : String(detectionError),
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function buildRtkInstallHint() {
|
|
146
|
+
if (platform === 'win32') {
|
|
147
|
+
return 'Install the external token optimizer binary for Windows, extract it, and ensure the executable is on PATH.';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (platform === 'darwin') {
|
|
151
|
+
return 'Install the external token optimizer with Homebrew, then verify the executable is available in your shell.';
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return 'Install the external token optimizer with the vendor installer, then verify it is available in your shell PATH.';
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export function buildRtkHookCommand(selectedAgentName) {
|
|
158
|
+
const normalizedAgentName = normalizeAgentName(selectedAgentName);
|
|
159
|
+
|
|
160
|
+
if (normalizedAgentName === 'copilot') {
|
|
161
|
+
return 'rtk init -g --copilot';
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (normalizedAgentName === 'claude') {
|
|
165
|
+
return 'rtk init -g';
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
if (normalizedAgentName === 'cursor') {
|
|
169
|
+
return 'rtk init -g --agent cursor';
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (normalizedAgentName === 'windsurf') {
|
|
173
|
+
return 'rtk init --agent windsurf';
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (normalizedAgentName === 'gemini') {
|
|
177
|
+
return 'rtk init -g --gemini';
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (normalizedAgentName === 'codex') {
|
|
181
|
+
return 'rtk init -g --codex';
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (normalizedAgentName === 'cline') {
|
|
185
|
+
return 'rtk init --agent cline';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return 'rtk init -g --copilot';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export function createTokenOptimizationState({
|
|
192
|
+
isEnabled,
|
|
193
|
+
selectedAgentName,
|
|
194
|
+
rtkDetection,
|
|
195
|
+
}) {
|
|
196
|
+
return {
|
|
197
|
+
schemaVersion: TOKEN_OPTIMIZATION_SCHEMA_VERSION,
|
|
198
|
+
enabled: Boolean(isEnabled),
|
|
199
|
+
selectedAgent: normalizeAgentName(selectedAgentName),
|
|
200
|
+
preferredShellProxy: rtkDetection.isAvailable ? 'external-proxy' : 'native-fallback',
|
|
201
|
+
externalProxy: {
|
|
202
|
+
detected: Boolean(rtkDetection.isAvailable),
|
|
203
|
+
version: rtkDetection.version,
|
|
204
|
+
detectionError: rtkDetection.detectionError,
|
|
205
|
+
},
|
|
206
|
+
commandRewriteMappings: COMMAND_REWRITE_MAPPINGS.map((mapping) => ({ ...mapping })),
|
|
207
|
+
generatedAt: new Date().toISOString(),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export async function readTokenOptimizationState(targetDirectoryPath) {
|
|
212
|
+
const stateFilePath = path.join(
|
|
213
|
+
targetDirectoryPath,
|
|
214
|
+
'.agent-context',
|
|
215
|
+
'state',
|
|
216
|
+
TOKEN_OPTIMIZATION_STATE_FILE_NAME
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
if (!(await pathExists(stateFilePath))) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
try {
|
|
224
|
+
const stateContent = await fs.readFile(stateFilePath, 'utf8');
|
|
225
|
+
const parsedState = JSON.parse(stateContent);
|
|
226
|
+
if (typeof parsedState.enabled !== 'boolean') {
|
|
227
|
+
return null;
|
|
228
|
+
}
|
|
229
|
+
return parsedState;
|
|
230
|
+
} catch {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export async function writeTokenOptimizationState(targetDirectoryPath, tokenOptimizationState) {
|
|
236
|
+
const stateDirectoryPath = path.join(targetDirectoryPath, '.agent-context', 'state');
|
|
237
|
+
const stateFilePath = path.join(stateDirectoryPath, TOKEN_OPTIMIZATION_STATE_FILE_NAME);
|
|
238
|
+
|
|
239
|
+
await fs.mkdir(stateDirectoryPath, { recursive: true });
|
|
240
|
+
await fs.writeFile(stateFilePath, JSON.stringify(tokenOptimizationState, null, 2) + '\n', 'utf8');
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export function buildTokenOptimizationGuidanceBlock(tokenOptimizationState) {
|
|
244
|
+
if (!tokenOptimizationState?.enabled) {
|
|
245
|
+
return [
|
|
246
|
+
'Token optimization mode is disabled for this repository.',
|
|
247
|
+
'Use native shell commands with manual output limiting when needed.',
|
|
248
|
+
].join('\n');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const rewriteLines = (tokenOptimizationState.commandRewriteMappings || []).map(
|
|
252
|
+
(mapping) => `- ${mapping.rawCommand} => ${mapping.optimizedCommand} (${mapping.reason})`
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const fallbackGuidance = [
|
|
256
|
+
'- Prefer command variants with bounded output (for example: git diff --stat, rg --max-count, and npm test -- --reporter=dot).',
|
|
257
|
+
'- Request only the lines or sections required for the current decision.',
|
|
258
|
+
'- If shell output is still large, summarize and continue iteratively instead of dumping full logs.',
|
|
259
|
+
];
|
|
260
|
+
|
|
261
|
+
return [
|
|
262
|
+
`Token optimization mode is enabled for agent: ${tokenOptimizationState.selectedAgent}.`,
|
|
263
|
+
`Preferred shell proxy: ${tokenOptimizationState.preferredShellProxy}.`,
|
|
264
|
+
'',
|
|
265
|
+
'Apply command rewrites before running verbose shell commands:',
|
|
266
|
+
...rewriteLines,
|
|
267
|
+
'',
|
|
268
|
+
'Important scope note:',
|
|
269
|
+
'- Shell rewrite hooks affect shell tool calls only.',
|
|
270
|
+
'- Built-in read/grep/glob style tools may bypass shell rewrites, so explicit compact shell commands should be preferred in high-volume sessions.',
|
|
271
|
+
'',
|
|
272
|
+
'Fallback behavior when external proxy is unavailable:',
|
|
273
|
+
...fallbackGuidance,
|
|
274
|
+
].join('\n');
|
|
275
|
+
}
|