sequant 1.15.2 → 1.15.3
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-plugin/plugin.json +1 -1
- package/dist/bin/cli.js +3 -0
- package/dist/src/commands/logs.js +15 -0
- package/dist/src/commands/run.d.ts +114 -1
- package/dist/src/commands/run.js +447 -23
- package/dist/src/commands/stats.js +48 -0
- package/dist/src/lib/scope/index.d.ts +1 -0
- package/dist/src/lib/scope/index.js +2 -0
- package/dist/src/lib/scope/settings-converter.d.ts +28 -0
- package/dist/src/lib/scope/settings-converter.js +53 -0
- package/dist/src/lib/settings.d.ts +45 -0
- package/dist/src/lib/settings.js +30 -0
- package/dist/src/lib/test-tautology-detector.d.ts +122 -0
- package/dist/src/lib/test-tautology-detector.js +488 -0
- package/dist/src/lib/workflow/git-diff-utils.d.ts +39 -0
- package/dist/src/lib/workflow/git-diff-utils.js +142 -0
- package/dist/src/lib/workflow/log-writer.d.ts +9 -2
- package/dist/src/lib/workflow/log-writer.js +9 -3
- package/dist/src/lib/workflow/metrics-schema.d.ts +9 -0
- package/dist/src/lib/workflow/metrics-schema.js +10 -1
- package/dist/src/lib/workflow/phase-detection.d.ts +3 -0
- package/dist/src/lib/workflow/phase-detection.js +27 -1
- package/dist/src/lib/workflow/qa-cache.d.ts +3 -1
- package/dist/src/lib/workflow/qa-cache.js +2 -0
- package/dist/src/lib/workflow/run-log-schema.d.ts +86 -3
- package/dist/src/lib/workflow/run-log-schema.js +40 -2
- package/dist/src/lib/workflow/state-utils.d.ts +46 -0
- package/dist/src/lib/workflow/state-utils.js +167 -0
- package/dist/src/lib/workflow/token-utils.d.ts +92 -0
- package/dist/src/lib/workflow/token-utils.js +170 -0
- package/dist/src/lib/workflow/types.d.ts +6 -0
- package/dist/src/lib/workflow/types.js +1 -0
- package/package.json +1 -1
- package/templates/hooks/pre-tool.sh +4 -0
- package/templates/skills/assess/SKILL.md +1 -1
- package/templates/skills/exec/SKILL.md +5 -4
- package/templates/skills/improve/SKILL.md +37 -24
- package/templates/skills/loop/SKILL.md +3 -3
- package/templates/skills/qa/references/code-review-checklist.md +10 -11
- package/templates/skills/qa/scripts/quality-checks.sh +16 -0
- package/templates/skills/security-review/references/security-checklists.md +89 -36
- package/templates/skills/solve/SKILL.md +3 -1
- package/templates/skills/spec/SKILL.md +8 -4
|
@@ -248,6 +248,13 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
248
248
|
chainModeSuccessRate: 0,
|
|
249
249
|
singleIssueSuccessRate: 0,
|
|
250
250
|
insights: [],
|
|
251
|
+
// Token breakdown
|
|
252
|
+
totalInputTokens: 0,
|
|
253
|
+
totalOutputTokens: 0,
|
|
254
|
+
totalCacheTokens: 0,
|
|
255
|
+
avgInputTokens: 0,
|
|
256
|
+
avgOutputTokens: 0,
|
|
257
|
+
avgCacheTokens: 0,
|
|
251
258
|
};
|
|
252
259
|
}
|
|
253
260
|
const successCount = runs.filter((r) => r.outcome === "success").length;
|
|
@@ -273,6 +280,10 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
273
280
|
: 0;
|
|
274
281
|
// Generate insights
|
|
275
282
|
const insights = generateInsights(runs, successRate, avgFilesChanged, avgLinesAdded, chainModeSuccessRate, singleIssueSuccessRate);
|
|
283
|
+
// Token breakdown (AC-10)
|
|
284
|
+
const totalInputTokens = runs.reduce((sum, r) => sum + (r.metrics.inputTokens ?? 0), 0);
|
|
285
|
+
const totalOutputTokens = runs.reduce((sum, r) => sum + (r.metrics.outputTokens ?? 0), 0);
|
|
286
|
+
const totalCacheTokens = runs.reduce((sum, r) => sum + (r.metrics.cacheTokens ?? 0), 0);
|
|
276
287
|
return {
|
|
277
288
|
totalRuns: runs.length,
|
|
278
289
|
successCount,
|
|
@@ -286,6 +297,13 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
286
297
|
chainModeSuccessRate,
|
|
287
298
|
singleIssueSuccessRate,
|
|
288
299
|
insights,
|
|
300
|
+
// Token breakdown
|
|
301
|
+
totalInputTokens,
|
|
302
|
+
totalOutputTokens,
|
|
303
|
+
totalCacheTokens,
|
|
304
|
+
avgInputTokens: runs.length > 0 ? totalInputTokens / runs.length : 0,
|
|
305
|
+
avgOutputTokens: runs.length > 0 ? totalOutputTokens / runs.length : 0,
|
|
306
|
+
avgCacheTokens: runs.length > 0 ? totalCacheTokens / runs.length : 0,
|
|
289
307
|
};
|
|
290
308
|
}
|
|
291
309
|
/**
|
|
@@ -375,6 +393,27 @@ function displayMetricsAnalytics(analytics) {
|
|
|
375
393
|
}
|
|
376
394
|
avgData["Duration"] = formatDuration(analytics.avgDuration);
|
|
377
395
|
console.log(ui.keyValueTable(avgData));
|
|
396
|
+
// Token breakdown (AC-10)
|
|
397
|
+
const hasTokenBreakdown = analytics.totalInputTokens > 0 ||
|
|
398
|
+
analytics.totalOutputTokens > 0 ||
|
|
399
|
+
analytics.totalCacheTokens > 0;
|
|
400
|
+
if (hasTokenBreakdown) {
|
|
401
|
+
console.log(ui.sectionHeader("Token Usage"));
|
|
402
|
+
const tokenData = {};
|
|
403
|
+
if (analytics.totalInputTokens > 0) {
|
|
404
|
+
tokenData["Input tokens"] = analytics.totalInputTokens.toLocaleString();
|
|
405
|
+
tokenData["Avg input/run"] = Math.round(analytics.avgInputTokens).toLocaleString();
|
|
406
|
+
}
|
|
407
|
+
if (analytics.totalOutputTokens > 0) {
|
|
408
|
+
tokenData["Output tokens"] = analytics.totalOutputTokens.toLocaleString();
|
|
409
|
+
tokenData["Avg output/run"] = Math.round(analytics.avgOutputTokens).toLocaleString();
|
|
410
|
+
}
|
|
411
|
+
if (analytics.totalCacheTokens > 0) {
|
|
412
|
+
tokenData["Cache tokens"] = analytics.totalCacheTokens.toLocaleString();
|
|
413
|
+
tokenData["Avg cache/run"] = Math.round(analytics.avgCacheTokens).toLocaleString();
|
|
414
|
+
}
|
|
415
|
+
console.log(ui.keyValueTable(tokenData));
|
|
416
|
+
}
|
|
378
417
|
// Insights
|
|
379
418
|
if (analytics.insights.length > 0) {
|
|
380
419
|
console.log(ui.sectionHeader("Insights"));
|
|
@@ -409,6 +448,15 @@ export async function statsCommand(options) {
|
|
|
409
448
|
chainModeSuccessRate: analytics.chainModeSuccessRate,
|
|
410
449
|
singleIssueSuccessRate: analytics.singleIssueSuccessRate,
|
|
411
450
|
insights: analytics.insights,
|
|
451
|
+
// Token breakdown (AC-10)
|
|
452
|
+
tokenBreakdown: {
|
|
453
|
+
totalInputTokens: analytics.totalInputTokens,
|
|
454
|
+
totalOutputTokens: analytics.totalOutputTokens,
|
|
455
|
+
totalCacheTokens: analytics.totalCacheTokens,
|
|
456
|
+
avgInputTokens: analytics.avgInputTokens,
|
|
457
|
+
avgOutputTokens: analytics.avgOutputTokens,
|
|
458
|
+
avgCacheTokens: analytics.avgCacheTokens,
|
|
459
|
+
},
|
|
412
460
|
runs: metrics.runs,
|
|
413
461
|
};
|
|
414
462
|
console.log(JSON.stringify(output, null, 2));
|
|
@@ -22,6 +22,7 @@ export { DEFAULT_SCOPE_CONFIG, ScopeAssessmentSchema } from "./types.js";
|
|
|
22
22
|
export { clusterACByKeyword, detectTitleVerbs, estimateDirectorySpread, calculateFeatureCount, detectFeatures, parseNonGoals, shouldSkipAssessment, } from "./analyzer.js";
|
|
23
23
|
export { getMetricStatus, createScopeMetrics, calculateVerdict, generateRecommendation, getVerdictEmoji, getStatusEmoji, shouldEnableQualityLoop, } from "./verdict.js";
|
|
24
24
|
export { formatNonGoals, formatMetricsTable, formatVerdict, formatScopeAssessment, formatCondensedAssessment, } from "./formatter.js";
|
|
25
|
+
export { convertSettingsToConfig } from "./settings-converter.js";
|
|
25
26
|
/**
|
|
26
27
|
* Perform complete scope assessment
|
|
27
28
|
*
|
|
@@ -25,6 +25,8 @@ export { clusterACByKeyword, detectTitleVerbs, estimateDirectorySpread, calculat
|
|
|
25
25
|
export { getMetricStatus, createScopeMetrics, calculateVerdict, generateRecommendation, getVerdictEmoji, getStatusEmoji, shouldEnableQualityLoop, } from "./verdict.js";
|
|
26
26
|
// Re-export formatter functions
|
|
27
27
|
export { formatNonGoals, formatMetricsTable, formatVerdict, formatScopeAssessment, formatCondensedAssessment, } from "./formatter.js";
|
|
28
|
+
// Re-export settings converter
|
|
29
|
+
export { convertSettingsToConfig } from "./settings-converter.js";
|
|
28
30
|
/**
|
|
29
31
|
* Perform complete scope assessment
|
|
30
32
|
*
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings to Config Converter
|
|
3
|
+
*
|
|
4
|
+
* Converts ScopeAssessmentSettings (from .sequant/settings.json)
|
|
5
|
+
* to ScopeAssessmentConfig (used by performScopeAssessment).
|
|
6
|
+
*
|
|
7
|
+
* This enables users to configure custom scope thresholds
|
|
8
|
+
* via their project settings file.
|
|
9
|
+
*/
|
|
10
|
+
import type { ScopeAssessmentSettings } from "../settings.js";
|
|
11
|
+
import type { ScopeAssessmentConfig } from "./types.js";
|
|
12
|
+
/**
|
|
13
|
+
* Convert ScopeAssessmentSettings to ScopeAssessmentConfig
|
|
14
|
+
*
|
|
15
|
+
* Merges user settings with defaults to produce a valid config.
|
|
16
|
+
* Any missing fields are filled from DEFAULT_SCOPE_CONFIG.
|
|
17
|
+
*
|
|
18
|
+
* @param settings - User settings from .sequant/settings.json
|
|
19
|
+
* @returns Complete config ready for performScopeAssessment
|
|
20
|
+
*
|
|
21
|
+
* @example
|
|
22
|
+
* ```typescript
|
|
23
|
+
* const settings = await getSettings();
|
|
24
|
+
* const config = convertSettingsToConfig(settings.scopeAssessment);
|
|
25
|
+
* const assessment = performScopeAssessment(criteria, body, title, config);
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export declare function convertSettingsToConfig(settings?: Partial<ScopeAssessmentSettings>): ScopeAssessmentConfig;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Settings to Config Converter
|
|
3
|
+
*
|
|
4
|
+
* Converts ScopeAssessmentSettings (from .sequant/settings.json)
|
|
5
|
+
* to ScopeAssessmentConfig (used by performScopeAssessment).
|
|
6
|
+
*
|
|
7
|
+
* This enables users to configure custom scope thresholds
|
|
8
|
+
* via their project settings file.
|
|
9
|
+
*/
|
|
10
|
+
import { DEFAULT_SCOPE_CONFIG } from "./types.js";
|
|
11
|
+
/**
|
|
12
|
+
* Convert ScopeAssessmentSettings to ScopeAssessmentConfig
|
|
13
|
+
*
|
|
14
|
+
* Merges user settings with defaults to produce a valid config.
|
|
15
|
+
* Any missing fields are filled from DEFAULT_SCOPE_CONFIG.
|
|
16
|
+
*
|
|
17
|
+
* @param settings - User settings from .sequant/settings.json
|
|
18
|
+
* @returns Complete config ready for performScopeAssessment
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const settings = await getSettings();
|
|
23
|
+
* const config = convertSettingsToConfig(settings.scopeAssessment);
|
|
24
|
+
* const assessment = performScopeAssessment(criteria, body, title, config);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function convertSettingsToConfig(settings) {
|
|
28
|
+
// Handle undefined/null settings
|
|
29
|
+
if (!settings) {
|
|
30
|
+
return DEFAULT_SCOPE_CONFIG;
|
|
31
|
+
}
|
|
32
|
+
// Helper to merge threshold with defaults
|
|
33
|
+
const mergeThreshold = (userThreshold, defaultThreshold) => ({
|
|
34
|
+
yellow: userThreshold?.yellow ?? defaultThreshold?.yellow ?? 0,
|
|
35
|
+
red: userThreshold?.red ?? defaultThreshold?.red ?? 0,
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
enabled: settings.enabled ?? DEFAULT_SCOPE_CONFIG.enabled,
|
|
39
|
+
skipIfSimple: settings.skipIfSimple ?? DEFAULT_SCOPE_CONFIG.skipIfSimple,
|
|
40
|
+
trivialThresholds: {
|
|
41
|
+
maxACItems: settings.trivialThresholds?.maxACItems ??
|
|
42
|
+
DEFAULT_SCOPE_CONFIG.trivialThresholds.maxACItems,
|
|
43
|
+
maxDirectories: settings.trivialThresholds?.maxDirectories ??
|
|
44
|
+
DEFAULT_SCOPE_CONFIG.trivialThresholds.maxDirectories,
|
|
45
|
+
},
|
|
46
|
+
thresholds: {
|
|
47
|
+
featureCount: mergeThreshold(settings.thresholds?.featureCount, DEFAULT_SCOPE_CONFIG.thresholds.featureCount),
|
|
48
|
+
acItems: mergeThreshold(settings.thresholds?.acItems, DEFAULT_SCOPE_CONFIG.thresholds.acItems),
|
|
49
|
+
fileEstimate: mergeThreshold(settings.thresholds?.fileEstimate, DEFAULT_SCOPE_CONFIG.thresholds.fileEstimate),
|
|
50
|
+
directorySpread: mergeThreshold(settings.thresholds?.directorySpread, DEFAULT_SCOPE_CONFIG.thresholds.directorySpread),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -80,6 +80,13 @@ export interface RunSettings {
|
|
|
80
80
|
* Default: true
|
|
81
81
|
*/
|
|
82
82
|
mcp: boolean;
|
|
83
|
+
/**
|
|
84
|
+
* Enable automatic retry with MCP fallback.
|
|
85
|
+
* When true (default), failed phases are retried with MCP disabled.
|
|
86
|
+
* When false or --no-retry flag is used, no retry attempts are made.
|
|
87
|
+
* Default: true
|
|
88
|
+
*/
|
|
89
|
+
retry: boolean;
|
|
83
90
|
}
|
|
84
91
|
/**
|
|
85
92
|
* Scope assessment threshold configuration
|
|
@@ -90,14 +97,40 @@ export interface ScopeThreshold {
|
|
|
90
97
|
/** Value at which status becomes red */
|
|
91
98
|
red: number;
|
|
92
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* Trivial issue thresholds for skipping scope assessment
|
|
102
|
+
*/
|
|
103
|
+
export interface TrivialThresholds {
|
|
104
|
+
/**
|
|
105
|
+
* Maximum AC items for trivial classification.
|
|
106
|
+
* Issues with fewer AC items are considered trivial.
|
|
107
|
+
* Default: 3
|
|
108
|
+
*/
|
|
109
|
+
maxACItems: number;
|
|
110
|
+
/**
|
|
111
|
+
* Maximum directories touched for trivial classification.
|
|
112
|
+
* Issues affecting fewer directories are considered trivial.
|
|
113
|
+
* Default: 1
|
|
114
|
+
*/
|
|
115
|
+
maxDirectories: number;
|
|
116
|
+
}
|
|
93
117
|
/**
|
|
94
118
|
* Scope assessment settings
|
|
119
|
+
*
|
|
120
|
+
* Configuration for scope assessment during /spec phase.
|
|
121
|
+
* These settings control how issue scope is evaluated and
|
|
122
|
+
* what thresholds trigger warnings.
|
|
95
123
|
*/
|
|
96
124
|
export interface ScopeAssessmentSettings {
|
|
97
125
|
/** Whether scope assessment is enabled (default: true) */
|
|
98
126
|
enabled: boolean;
|
|
99
127
|
/** Skip assessment for trivial issues (default: true) */
|
|
100
128
|
skipIfSimple: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* Trivial issue thresholds (skip if below all).
|
|
131
|
+
* Issues that fall below all these thresholds are skipped.
|
|
132
|
+
*/
|
|
133
|
+
trivialThresholds: TrivialThresholds;
|
|
101
134
|
/** Thresholds for scope metrics */
|
|
102
135
|
thresholds: {
|
|
103
136
|
/** Feature count thresholds (default: yellow=2, red=3) */
|
|
@@ -106,6 +139,8 @@ export interface ScopeAssessmentSettings {
|
|
|
106
139
|
acItems: ScopeThreshold;
|
|
107
140
|
/** File estimate thresholds (default: yellow=8, red=13) */
|
|
108
141
|
fileEstimate: ScopeThreshold;
|
|
142
|
+
/** Directory spread thresholds (default: yellow=3, red=5) */
|
|
143
|
+
directorySpread: ScopeThreshold;
|
|
109
144
|
};
|
|
110
145
|
}
|
|
111
146
|
/**
|
|
@@ -129,8 +164,18 @@ export declare const DEFAULT_ROTATION_SETTINGS: RotationSettings;
|
|
|
129
164
|
* Default agent settings (cost-optimized)
|
|
130
165
|
*/
|
|
131
166
|
export declare const DEFAULT_AGENT_SETTINGS: AgentSettings;
|
|
167
|
+
/**
|
|
168
|
+
* Default trivial thresholds for scope assessment
|
|
169
|
+
*
|
|
170
|
+
* Issues that fall below ALL of these thresholds are considered trivial
|
|
171
|
+
* and scope assessment is skipped.
|
|
172
|
+
*/
|
|
173
|
+
export declare const DEFAULT_TRIVIAL_THRESHOLDS: TrivialThresholds;
|
|
132
174
|
/**
|
|
133
175
|
* Default scope assessment settings
|
|
176
|
+
*
|
|
177
|
+
* These defaults match the values in DEFAULT_SCOPE_CONFIG from
|
|
178
|
+
* src/lib/scope/types.ts to ensure consistency.
|
|
134
179
|
*/
|
|
135
180
|
export declare const DEFAULT_SCOPE_ASSESSMENT_SETTINGS: ScopeAssessmentSettings;
|
|
136
181
|
/**
|
package/dist/src/lib/settings.js
CHANGED
|
@@ -31,16 +31,41 @@ export const DEFAULT_AGENT_SETTINGS = {
|
|
|
31
31
|
parallel: false,
|
|
32
32
|
model: "haiku",
|
|
33
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* Default trivial thresholds for scope assessment
|
|
36
|
+
*
|
|
37
|
+
* Issues that fall below ALL of these thresholds are considered trivial
|
|
38
|
+
* and scope assessment is skipped.
|
|
39
|
+
*/
|
|
40
|
+
export const DEFAULT_TRIVIAL_THRESHOLDS = {
|
|
41
|
+
/** Issues with 3 or fewer AC items are potentially trivial */
|
|
42
|
+
maxACItems: 3,
|
|
43
|
+
/** Issues touching only 1 directory are potentially trivial */
|
|
44
|
+
maxDirectories: 1,
|
|
45
|
+
};
|
|
34
46
|
/**
|
|
35
47
|
* Default scope assessment settings
|
|
48
|
+
*
|
|
49
|
+
* These defaults match the values in DEFAULT_SCOPE_CONFIG from
|
|
50
|
+
* src/lib/scope/types.ts to ensure consistency.
|
|
36
51
|
*/
|
|
37
52
|
export const DEFAULT_SCOPE_ASSESSMENT_SETTINGS = {
|
|
53
|
+
/** Enable scope assessment by default */
|
|
38
54
|
enabled: true,
|
|
55
|
+
/** Skip assessment for trivial issues by default */
|
|
39
56
|
skipIfSimple: true,
|
|
57
|
+
/** Trivial issue thresholds - skip if below all */
|
|
58
|
+
trivialThresholds: DEFAULT_TRIVIAL_THRESHOLDS,
|
|
59
|
+
/** Thresholds for scope metrics */
|
|
40
60
|
thresholds: {
|
|
61
|
+
/** 2 features = yellow warning, 3+ = red (split recommended) */
|
|
41
62
|
featureCount: { yellow: 2, red: 3 },
|
|
63
|
+
/** 6-8 AC items = yellow, 9+ = red */
|
|
42
64
|
acItems: { yellow: 6, red: 9 },
|
|
65
|
+
/** 8-12 files estimated = yellow, 13+ = red */
|
|
43
66
|
fileEstimate: { yellow: 8, red: 13 },
|
|
67
|
+
/** 3-4 directories = yellow, 5+ = red */
|
|
68
|
+
directorySpread: { yellow: 3, red: 5 },
|
|
44
69
|
},
|
|
45
70
|
};
|
|
46
71
|
/**
|
|
@@ -59,6 +84,7 @@ export const DEFAULT_SETTINGS = {
|
|
|
59
84
|
smartTests: true,
|
|
60
85
|
rotation: DEFAULT_ROTATION_SETTINGS,
|
|
61
86
|
mcp: true, // Enable MCP servers by default in headless mode
|
|
87
|
+
retry: true, // Enable automatic retry with MCP fallback by default
|
|
62
88
|
},
|
|
63
89
|
agents: DEFAULT_AGENT_SETTINGS,
|
|
64
90
|
scopeAssessment: DEFAULT_SCOPE_ASSESSMENT_SETTINGS,
|
|
@@ -89,6 +115,10 @@ export async function getSettings() {
|
|
|
89
115
|
scopeAssessment: {
|
|
90
116
|
...DEFAULT_SCOPE_ASSESSMENT_SETTINGS,
|
|
91
117
|
...parsed.scopeAssessment,
|
|
118
|
+
trivialThresholds: {
|
|
119
|
+
...DEFAULT_SCOPE_ASSESSMENT_SETTINGS.trivialThresholds,
|
|
120
|
+
...parsed.scopeAssessment?.trivialThresholds,
|
|
121
|
+
},
|
|
92
122
|
thresholds: {
|
|
93
123
|
...DEFAULT_SCOPE_ASSESSMENT_SETTINGS.thresholds,
|
|
94
124
|
...parsed.scopeAssessment?.thresholds,
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test Tautology Detector
|
|
3
|
+
*
|
|
4
|
+
* Detects tautological tests — tests that pass but don't call any production code.
|
|
5
|
+
* These tests provide zero regression protection as they only assert on local values.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { detectTautologicalTests, formatTautologyResults } from './test-tautology-detector';
|
|
10
|
+
*
|
|
11
|
+
* const results = detectTautologicalTests([
|
|
12
|
+
* { path: 'src/lib/foo.test.ts', content: fileContent },
|
|
13
|
+
* ]);
|
|
14
|
+
* console.log(formatTautologyResults(results));
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
/**
|
|
18
|
+
* Represents an imported function from a source module
|
|
19
|
+
*/
|
|
20
|
+
export interface ImportedFunction {
|
|
21
|
+
/** Function name */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Module path the function was imported from */
|
|
24
|
+
modulePath: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Represents a test block (it() or test())
|
|
28
|
+
*/
|
|
29
|
+
export interface TestBlock {
|
|
30
|
+
/** Test description */
|
|
31
|
+
description: string;
|
|
32
|
+
/** Line number where the test starts */
|
|
33
|
+
lineNumber: number;
|
|
34
|
+
/** Whether this test is tautological (no production function calls) */
|
|
35
|
+
isTautological: boolean;
|
|
36
|
+
/** Style of test block: 'it' or 'test' */
|
|
37
|
+
style: "it" | "test";
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Result of analyzing a single test file
|
|
41
|
+
*/
|
|
42
|
+
export interface TautologyFileResult {
|
|
43
|
+
/** Path to the test file */
|
|
44
|
+
filePath: string;
|
|
45
|
+
/** Total number of test blocks found */
|
|
46
|
+
totalTests: number;
|
|
47
|
+
/** Number of tautological test blocks */
|
|
48
|
+
tautologicalCount: number;
|
|
49
|
+
/** Percentage of tests that are tautological */
|
|
50
|
+
tautologicalPercentage: number;
|
|
51
|
+
/** Individual test blocks with their analysis */
|
|
52
|
+
testBlocks: TestBlock[];
|
|
53
|
+
/** Imported functions from source modules */
|
|
54
|
+
importedFunctions: ImportedFunction[];
|
|
55
|
+
/** Whether the file could be parsed successfully */
|
|
56
|
+
parseSuccess: boolean;
|
|
57
|
+
/** Error message if parsing failed */
|
|
58
|
+
parseError?: string;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Overall tautology detection results
|
|
62
|
+
*/
|
|
63
|
+
export interface TautologyResults {
|
|
64
|
+
/** Results for each analyzed file */
|
|
65
|
+
fileResults: TautologyFileResult[];
|
|
66
|
+
/** Summary statistics */
|
|
67
|
+
summary: {
|
|
68
|
+
totalFiles: number;
|
|
69
|
+
totalTests: number;
|
|
70
|
+
totalTautological: number;
|
|
71
|
+
overallPercentage: number;
|
|
72
|
+
/** Whether >50% of tests are tautological (blocking threshold) */
|
|
73
|
+
exceedsBlockingThreshold: boolean;
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if an import path is from a source module (not a test library or mock)
|
|
78
|
+
*/
|
|
79
|
+
export declare function isSourceModule(modulePath: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Extract imports from a test file
|
|
82
|
+
*
|
|
83
|
+
* Handles:
|
|
84
|
+
* - Named imports: `import { foo, bar } from './module'`
|
|
85
|
+
* - Default imports: `import foo from './module'`
|
|
86
|
+
* - Namespace imports: `import * as foo from './module'` (extracts the namespace name)
|
|
87
|
+
*/
|
|
88
|
+
export declare function extractImports(content: string): ImportedFunction[];
|
|
89
|
+
/**
|
|
90
|
+
* Extract test blocks (it() and test()) from content
|
|
91
|
+
*
|
|
92
|
+
* Returns the description, line number, body content, and style of each test block.
|
|
93
|
+
*/
|
|
94
|
+
export declare function extractTestBlocks(content: string): Array<{
|
|
95
|
+
description: string;
|
|
96
|
+
lineNumber: number;
|
|
97
|
+
body: string;
|
|
98
|
+
style: "it" | "test";
|
|
99
|
+
}>;
|
|
100
|
+
/**
|
|
101
|
+
* Check if a test block contains calls to any of the imported production functions
|
|
102
|
+
*/
|
|
103
|
+
export declare function testBlockCallsProductionCode(body: string, importedFunctions: ImportedFunction[]): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Analyze a single test file for tautological tests
|
|
106
|
+
*/
|
|
107
|
+
export declare function analyzeTestFile(content: string, filePath: string): TautologyFileResult;
|
|
108
|
+
/**
|
|
109
|
+
* Detect tautological tests across multiple files
|
|
110
|
+
*/
|
|
111
|
+
export declare function detectTautologicalTests(files: Array<{
|
|
112
|
+
path: string;
|
|
113
|
+
content: string;
|
|
114
|
+
}>): TautologyResults;
|
|
115
|
+
/**
|
|
116
|
+
* Format tautology results as markdown for QA output
|
|
117
|
+
*/
|
|
118
|
+
export declare function formatTautologyResults(results: TautologyResults): string;
|
|
119
|
+
/**
|
|
120
|
+
* Determine verdict impact based on tautology results
|
|
121
|
+
*/
|
|
122
|
+
export declare function getTautologyVerdictImpact(results: TautologyResults): "none" | "warning" | "blocking";
|