sequant 1.13.5 → 1.14.1

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.
Files changed (33) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/dist/bin/cli.js +1 -0
  3. package/dist/src/commands/run.d.ts +21 -0
  4. package/dist/src/commands/run.js +51 -1
  5. package/dist/src/index.d.ts +2 -1
  6. package/dist/src/index.js +2 -0
  7. package/dist/src/lib/cli-ui.d.ts +1 -1
  8. package/dist/src/lib/cli-ui.js +12 -14
  9. package/dist/src/lib/scope/analyzer.d.ts +91 -0
  10. package/dist/src/lib/scope/analyzer.js +310 -0
  11. package/dist/src/lib/scope/formatter.d.ts +52 -0
  12. package/dist/src/lib/scope/formatter.js +169 -0
  13. package/dist/src/lib/scope/index.d.ts +36 -0
  14. package/dist/src/lib/scope/index.js +73 -0
  15. package/dist/src/lib/scope/types.d.ts +198 -0
  16. package/dist/src/lib/scope/types.js +60 -0
  17. package/dist/src/lib/scope/verdict.d.ts +80 -0
  18. package/dist/src/lib/scope/verdict.js +173 -0
  19. package/dist/src/lib/settings.d.ts +33 -0
  20. package/dist/src/lib/settings.js +21 -0
  21. package/dist/src/lib/workflow/phase-detection.d.ts +114 -0
  22. package/dist/src/lib/workflow/phase-detection.js +215 -0
  23. package/dist/src/lib/workflow/state-manager.d.ts +11 -0
  24. package/dist/src/lib/workflow/state-manager.js +26 -0
  25. package/dist/src/lib/workflow/state-schema.d.ts +103 -0
  26. package/dist/src/lib/workflow/state-schema.js +20 -0
  27. package/package.json +1 -1
  28. package/templates/skills/exec/SKILL.md +50 -1
  29. package/templates/skills/fullsolve/SKILL.md +47 -1
  30. package/templates/skills/qa/SKILL.md +47 -1
  31. package/templates/skills/security-review/SKILL.md +0 -1
  32. package/templates/skills/spec/SKILL.md +39 -1
  33. package/templates/skills/testgen/SKILL.md +1 -0
@@ -0,0 +1,169 @@
1
+ /**
2
+ * Scope Assessment Formatter
3
+ *
4
+ * Formats scope assessment results as markdown for /spec output
5
+ * and GitHub issue comments.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { formatScopeAssessment } from './formatter';
10
+ *
11
+ * const assessment = performScopeAssessment(criteria, issueBody, title);
12
+ * console.log(formatScopeAssessment(assessment));
13
+ * ```
14
+ */
15
+ import { getVerdictEmoji, getStatusEmoji } from "./verdict.js";
16
+ /**
17
+ * Format non-goals section for output
18
+ *
19
+ * @param nonGoals - Non-goals parsing result
20
+ * @returns Formatted markdown string
21
+ */
22
+ export function formatNonGoals(nonGoals) {
23
+ const lines = ["### Non-Goals (Required)", ""];
24
+ if (!nonGoals.found) {
25
+ lines.push("⚠️ **Non-Goals section not found.** Consider adding scope boundaries.");
26
+ lines.push("");
27
+ lines.push("Example format:");
28
+ lines.push("```markdown");
29
+ lines.push("## Non-Goals");
30
+ lines.push("");
31
+ lines.push("What this issue explicitly will NOT do:");
32
+ lines.push("- [ ] [Adjacent feature we're deferring]");
33
+ lines.push("- [ ] [Scope boundary we're respecting]");
34
+ lines.push("- [ ] [Future work that's out of scope]");
35
+ lines.push("```");
36
+ return lines.join("\n");
37
+ }
38
+ if (nonGoals.items.length === 0) {
39
+ lines.push("⚠️ **Non-Goals section is empty.** Add explicit scope boundaries.");
40
+ return lines.join("\n");
41
+ }
42
+ lines.push(`✅ ${nonGoals.items.length} non-goal(s) defined:`);
43
+ lines.push("");
44
+ for (const item of nonGoals.items) {
45
+ lines.push(`- ${item}`);
46
+ }
47
+ return lines.join("\n");
48
+ }
49
+ /**
50
+ * Format scope metrics table
51
+ *
52
+ * @param assessment - Complete scope assessment
53
+ * @returns Formatted markdown table
54
+ */
55
+ export function formatMetricsTable(assessment) {
56
+ const lines = [
57
+ "### Scope Metrics",
58
+ "",
59
+ "| Metric | Value | Status |",
60
+ "|--------|-------|--------|",
61
+ ];
62
+ for (const metric of assessment.metrics) {
63
+ const emoji = getStatusEmoji(metric.status);
64
+ lines.push(`| ${metric.name} | ${metric.value} | ${emoji} |`);
65
+ }
66
+ return lines.join("\n");
67
+ }
68
+ /**
69
+ * Format scope verdict section
70
+ *
71
+ * @param assessment - Complete scope assessment
72
+ * @returns Formatted markdown string
73
+ */
74
+ export function formatVerdict(assessment) {
75
+ const emoji = getVerdictEmoji(assessment.verdict);
76
+ const lines = [
77
+ "### Scope Verdict",
78
+ "",
79
+ `${emoji} **${assessment.verdict}** - ${assessment.recommendation}`,
80
+ ];
81
+ // Add split recommendation details if needed
82
+ if (assessment.verdict === "SCOPE_SPLIT_RECOMMENDED") {
83
+ lines.push("");
84
+ lines.push("**Suggested splits:**");
85
+ // Group ACs by cluster for split suggestions
86
+ const significantClusters = assessment.featureDetection.clusters.filter((c) => c.count >= 2);
87
+ if (significantClusters.length > 1) {
88
+ for (const cluster of significantClusters) {
89
+ lines.push(`- Issue for **${cluster.keyword}**: ${cluster.acIds.join(", ")}`);
90
+ }
91
+ }
92
+ else {
93
+ lines.push("- Consider separating by functional area");
94
+ lines.push("- Each issue should have a single deployable outcome");
95
+ }
96
+ }
97
+ return lines.join("\n");
98
+ }
99
+ /**
100
+ * Format complete scope assessment section
101
+ *
102
+ * @param assessment - Complete scope assessment
103
+ * @returns Formatted markdown string
104
+ */
105
+ export function formatScopeAssessment(assessment) {
106
+ if (assessment.skipped) {
107
+ return [
108
+ "## Scope Assessment",
109
+ "",
110
+ `*Skipped: ${assessment.skipReason}*`,
111
+ ].join("\n");
112
+ }
113
+ const sections = [
114
+ "## Scope Assessment",
115
+ "",
116
+ formatNonGoals(assessment.nonGoals),
117
+ "",
118
+ formatMetricsTable(assessment),
119
+ "",
120
+ formatVerdict(assessment),
121
+ ];
122
+ // Add quality loop recommendation if needed
123
+ if (assessment.verdict === "SCOPE_WARNING" ||
124
+ assessment.verdict === "SCOPE_SPLIT_RECOMMENDED") {
125
+ sections.push("");
126
+ sections.push("---");
127
+ sections.push("");
128
+ sections.push("**Quality Loop:** Will be auto-enabled due to scope concerns.");
129
+ }
130
+ return sections.join("\n");
131
+ }
132
+ /**
133
+ * Format condensed scope assessment for issue comments
134
+ *
135
+ * A shorter version suitable for GitHub issue comments.
136
+ *
137
+ * @param assessment - Complete scope assessment
138
+ * @returns Condensed markdown string
139
+ */
140
+ export function formatCondensedAssessment(assessment) {
141
+ if (assessment.skipped) {
142
+ return `**Scope Assessment:** Skipped (${assessment.skipReason})`;
143
+ }
144
+ const emoji = getVerdictEmoji(assessment.verdict);
145
+ const lines = [
146
+ `### Scope Assessment: ${emoji} ${assessment.verdict}`,
147
+ ];
148
+ // Metrics in single line
149
+ const metricsLine = assessment.metrics
150
+ .map((m) => `${m.name}: ${m.value} ${getStatusEmoji(m.status)}`)
151
+ .join(" | ");
152
+ lines.push("");
153
+ lines.push(metricsLine);
154
+ // Non-goals summary
155
+ if (!assessment.nonGoals.found || assessment.nonGoals.items.length === 0) {
156
+ lines.push("");
157
+ lines.push("⚠️ Non-Goals: Not defined");
158
+ }
159
+ else {
160
+ lines.push("");
161
+ lines.push(`✅ Non-Goals: ${assessment.nonGoals.items.length} defined`);
162
+ }
163
+ // Recommendation if not OK
164
+ if (assessment.verdict !== "SCOPE_OK") {
165
+ lines.push("");
166
+ lines.push(`*${assessment.recommendation}*`);
167
+ }
168
+ return lines.join("\n");
169
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Scope Assessment Module
3
+ *
4
+ * Provides scope analysis for /spec to catch overscoped issues early.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { performScopeAssessment, formatScopeAssessment } from './scope';
9
+ *
10
+ * const criteria = parseAcceptanceCriteria(issueBody);
11
+ * const assessment = performScopeAssessment(criteria, issueBody, issueTitle);
12
+ *
13
+ * if (assessment.verdict !== 'SCOPE_OK') {
14
+ * console.log(formatScopeAssessment(assessment));
15
+ * }
16
+ * ```
17
+ */
18
+ import type { AcceptanceCriterion } from "../workflow/state-schema.js";
19
+ import type { ScopeAssessment, ScopeAssessmentConfig } from "./types.js";
20
+ export type { ScopeVerdict, ScopeMetricStatus, ScopeMetric, ScopeAssessment, ScopeAssessmentConfig, MetricThreshold, ACCluster, FeatureDetection, NonGoals, } from "./types.js";
21
+ export { DEFAULT_SCOPE_CONFIG, ScopeAssessmentSchema } from "./types.js";
22
+ export { clusterACByKeyword, detectTitleVerbs, estimateDirectorySpread, calculateFeatureCount, detectFeatures, parseNonGoals, shouldSkipAssessment, } from "./analyzer.js";
23
+ export { getMetricStatus, createScopeMetrics, calculateVerdict, generateRecommendation, getVerdictEmoji, getStatusEmoji, shouldEnableQualityLoop, } from "./verdict.js";
24
+ export { formatNonGoals, formatMetricsTable, formatVerdict, formatScopeAssessment, formatCondensedAssessment, } from "./formatter.js";
25
+ /**
26
+ * Perform complete scope assessment
27
+ *
28
+ * This is the main entry point for scope assessment.
29
+ *
30
+ * @param criteria - Parsed acceptance criteria
31
+ * @param issueBody - Full issue body markdown
32
+ * @param title - Issue title
33
+ * @param config - Optional custom configuration
34
+ * @returns Complete scope assessment result
35
+ */
36
+ export declare function performScopeAssessment(criteria: AcceptanceCriterion[], issueBody: string, title: string, config?: ScopeAssessmentConfig): ScopeAssessment;
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Scope Assessment Module
3
+ *
4
+ * Provides scope analysis for /spec to catch overscoped issues early.
5
+ *
6
+ * @example
7
+ * ```typescript
8
+ * import { performScopeAssessment, formatScopeAssessment } from './scope';
9
+ *
10
+ * const criteria = parseAcceptanceCriteria(issueBody);
11
+ * const assessment = performScopeAssessment(criteria, issueBody, issueTitle);
12
+ *
13
+ * if (assessment.verdict !== 'SCOPE_OK') {
14
+ * console.log(formatScopeAssessment(assessment));
15
+ * }
16
+ * ```
17
+ */
18
+ import { DEFAULT_SCOPE_CONFIG } from "./types.js";
19
+ import { detectFeatures, parseNonGoals, shouldSkipAssessment, } from "./analyzer.js";
20
+ import { createScopeMetrics, calculateVerdict, generateRecommendation, } from "./verdict.js";
21
+ export { DEFAULT_SCOPE_CONFIG, ScopeAssessmentSchema } from "./types.js";
22
+ // Re-export analyzer functions
23
+ export { clusterACByKeyword, detectTitleVerbs, estimateDirectorySpread, calculateFeatureCount, detectFeatures, parseNonGoals, shouldSkipAssessment, } from "./analyzer.js";
24
+ // Re-export verdict functions
25
+ export { getMetricStatus, createScopeMetrics, calculateVerdict, generateRecommendation, getVerdictEmoji, getStatusEmoji, shouldEnableQualityLoop, } from "./verdict.js";
26
+ // Re-export formatter functions
27
+ export { formatNonGoals, formatMetricsTable, formatVerdict, formatScopeAssessment, formatCondensedAssessment, } from "./formatter.js";
28
+ /**
29
+ * Perform complete scope assessment
30
+ *
31
+ * This is the main entry point for scope assessment.
32
+ *
33
+ * @param criteria - Parsed acceptance criteria
34
+ * @param issueBody - Full issue body markdown
35
+ * @param title - Issue title
36
+ * @param config - Optional custom configuration
37
+ * @returns Complete scope assessment result
38
+ */
39
+ export function performScopeAssessment(criteria, issueBody, title, config = DEFAULT_SCOPE_CONFIG) {
40
+ // Parse non-goals first
41
+ const nonGoals = parseNonGoals(issueBody);
42
+ // Detect features
43
+ const featureDetection = detectFeatures(criteria, title);
44
+ // Check if we should skip (trivial issue)
45
+ const skipResult = shouldSkipAssessment(criteria.length, featureDetection.directorySpread, config);
46
+ if (skipResult.skip) {
47
+ return {
48
+ assessedAt: new Date().toISOString(),
49
+ skipped: true,
50
+ skipReason: skipResult.reason,
51
+ verdict: "SCOPE_OK",
52
+ metrics: [],
53
+ featureDetection,
54
+ nonGoals,
55
+ recommendation: "Trivial issue - scope assessment skipped.",
56
+ };
57
+ }
58
+ // Calculate metrics
59
+ const metrics = createScopeMetrics(featureDetection, criteria.length, config);
60
+ // Determine verdict
61
+ const verdict = calculateVerdict(metrics, nonGoals);
62
+ // Generate recommendation
63
+ const recommendation = generateRecommendation(verdict, metrics, featureDetection, nonGoals);
64
+ return {
65
+ assessedAt: new Date().toISOString(),
66
+ skipped: false,
67
+ verdict,
68
+ metrics,
69
+ featureDetection,
70
+ nonGoals,
71
+ recommendation,
72
+ };
73
+ }
@@ -0,0 +1,198 @@
1
+ /**
2
+ * Scope Assessment Types
3
+ *
4
+ * Types for the scope assessment system that evaluates issue scope
5
+ * during the /spec phase to catch overscoped issues early.
6
+ */
7
+ import { z } from "zod";
8
+ /**
9
+ * Scope verdict - overall assessment result
10
+ */
11
+ export type ScopeVerdict = "SCOPE_OK" | "SCOPE_WARNING" | "SCOPE_SPLIT_RECOMMENDED";
12
+ /**
13
+ * Scope metric status based on thresholds
14
+ */
15
+ export type ScopeMetricStatus = "green" | "yellow" | "red";
16
+ /**
17
+ * Threshold configuration for a single metric
18
+ */
19
+ export interface MetricThreshold {
20
+ /** Value at which status becomes yellow */
21
+ yellow: number;
22
+ /** Value at which status becomes red */
23
+ red: number;
24
+ }
25
+ /**
26
+ * Scope assessment configuration
27
+ */
28
+ export interface ScopeAssessmentConfig {
29
+ /** Whether scope assessment is enabled */
30
+ enabled: boolean;
31
+ /** Skip assessment for trivial issues */
32
+ skipIfSimple: boolean;
33
+ /** Trivial issue thresholds (skip if below all) */
34
+ trivialThresholds: {
35
+ /** Maximum AC items for trivial classification */
36
+ maxACItems: number;
37
+ /** Maximum directories touched for trivial classification */
38
+ maxDirectories: number;
39
+ };
40
+ /** Thresholds for scope metrics */
41
+ thresholds: {
42
+ /** Feature count thresholds */
43
+ featureCount: MetricThreshold;
44
+ /** AC items thresholds */
45
+ acItems: MetricThreshold;
46
+ /** File estimate thresholds */
47
+ fileEstimate: MetricThreshold;
48
+ /** Directory spread thresholds */
49
+ directorySpread: MetricThreshold;
50
+ };
51
+ }
52
+ /**
53
+ * Default scope assessment configuration
54
+ */
55
+ export declare const DEFAULT_SCOPE_CONFIG: ScopeAssessmentConfig;
56
+ /**
57
+ * Individual scope metric
58
+ */
59
+ export interface ScopeMetric {
60
+ /** Name of the metric */
61
+ name: string;
62
+ /** Current value */
63
+ value: number;
64
+ /** Status based on thresholds */
65
+ status: ScopeMetricStatus;
66
+ }
67
+ /**
68
+ * Clustering result for AC items
69
+ */
70
+ export interface ACCluster {
71
+ /** Cluster identifier (keyword-based) */
72
+ keyword: string;
73
+ /** AC IDs in this cluster */
74
+ acIds: string[];
75
+ /** Number of AC items in cluster */
76
+ count: number;
77
+ }
78
+ /**
79
+ * Feature detection result
80
+ */
81
+ export interface FeatureDetection {
82
+ /** Number of distinct features detected */
83
+ featureCount: number;
84
+ /** AC clusters by functional area */
85
+ clusters: ACCluster[];
86
+ /** Multiple verbs detected in title */
87
+ multipleVerbs: boolean;
88
+ /** Detected verb list from title */
89
+ titleVerbs: string[];
90
+ /** Directory spread detected */
91
+ directorySpread: number;
92
+ /** Detected directories from analysis */
93
+ directories: string[];
94
+ }
95
+ /**
96
+ * Non-goals section data
97
+ */
98
+ export interface NonGoals {
99
+ /** List of non-goal items */
100
+ items: string[];
101
+ /** Whether non-goals section was found */
102
+ found: boolean;
103
+ /** Warning message if empty or missing */
104
+ warning?: string;
105
+ }
106
+ /**
107
+ * Complete scope assessment result
108
+ */
109
+ export interface ScopeAssessment {
110
+ /** Timestamp of assessment */
111
+ assessedAt: string;
112
+ /** Whether assessment was skipped (trivial issue) */
113
+ skipped: boolean;
114
+ /** Reason for skipping (if skipped) */
115
+ skipReason?: string;
116
+ /** Overall verdict */
117
+ verdict: ScopeVerdict;
118
+ /** Individual metrics */
119
+ metrics: ScopeMetric[];
120
+ /** Feature detection details */
121
+ featureDetection: FeatureDetection;
122
+ /** Non-goals section */
123
+ nonGoals: NonGoals;
124
+ /** Recommendation message */
125
+ recommendation: string;
126
+ }
127
+ /**
128
+ * Zod schema for scope assessment (for state storage)
129
+ */
130
+ export declare const ScopeMetricSchema: z.ZodObject<{
131
+ name: z.ZodString;
132
+ value: z.ZodNumber;
133
+ status: z.ZodEnum<{
134
+ red: "red";
135
+ green: "green";
136
+ yellow: "yellow";
137
+ }>;
138
+ }, z.core.$strip>;
139
+ export declare const ACClusterSchema: z.ZodObject<{
140
+ keyword: z.ZodString;
141
+ acIds: z.ZodArray<z.ZodString>;
142
+ count: z.ZodNumber;
143
+ }, z.core.$strip>;
144
+ export declare const FeatureDetectionSchema: z.ZodObject<{
145
+ featureCount: z.ZodNumber;
146
+ clusters: z.ZodArray<z.ZodObject<{
147
+ keyword: z.ZodString;
148
+ acIds: z.ZodArray<z.ZodString>;
149
+ count: z.ZodNumber;
150
+ }, z.core.$strip>>;
151
+ multipleVerbs: z.ZodBoolean;
152
+ titleVerbs: z.ZodArray<z.ZodString>;
153
+ directorySpread: z.ZodNumber;
154
+ directories: z.ZodArray<z.ZodString>;
155
+ }, z.core.$strip>;
156
+ export declare const NonGoalsSchema: z.ZodObject<{
157
+ items: z.ZodArray<z.ZodString>;
158
+ found: z.ZodBoolean;
159
+ warning: z.ZodOptional<z.ZodString>;
160
+ }, z.core.$strip>;
161
+ export declare const ScopeAssessmentSchema: z.ZodObject<{
162
+ assessedAt: z.ZodString;
163
+ skipped: z.ZodBoolean;
164
+ skipReason: z.ZodOptional<z.ZodString>;
165
+ verdict: z.ZodEnum<{
166
+ SCOPE_OK: "SCOPE_OK";
167
+ SCOPE_WARNING: "SCOPE_WARNING";
168
+ SCOPE_SPLIT_RECOMMENDED: "SCOPE_SPLIT_RECOMMENDED";
169
+ }>;
170
+ metrics: z.ZodArray<z.ZodObject<{
171
+ name: z.ZodString;
172
+ value: z.ZodNumber;
173
+ status: z.ZodEnum<{
174
+ red: "red";
175
+ green: "green";
176
+ yellow: "yellow";
177
+ }>;
178
+ }, z.core.$strip>>;
179
+ featureDetection: z.ZodObject<{
180
+ featureCount: z.ZodNumber;
181
+ clusters: z.ZodArray<z.ZodObject<{
182
+ keyword: z.ZodString;
183
+ acIds: z.ZodArray<z.ZodString>;
184
+ count: z.ZodNumber;
185
+ }, z.core.$strip>>;
186
+ multipleVerbs: z.ZodBoolean;
187
+ titleVerbs: z.ZodArray<z.ZodString>;
188
+ directorySpread: z.ZodNumber;
189
+ directories: z.ZodArray<z.ZodString>;
190
+ }, z.core.$strip>;
191
+ nonGoals: z.ZodObject<{
192
+ items: z.ZodArray<z.ZodString>;
193
+ found: z.ZodBoolean;
194
+ warning: z.ZodOptional<z.ZodString>;
195
+ }, z.core.$strip>;
196
+ recommendation: z.ZodString;
197
+ }, z.core.$strip>;
198
+ export type ScopeMetricType = z.infer<typeof ScopeMetricSchema>;
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Scope Assessment Types
3
+ *
4
+ * Types for the scope assessment system that evaluates issue scope
5
+ * during the /spec phase to catch overscoped issues early.
6
+ */
7
+ import { z } from "zod";
8
+ /**
9
+ * Default scope assessment configuration
10
+ */
11
+ export const DEFAULT_SCOPE_CONFIG = {
12
+ enabled: true,
13
+ skipIfSimple: true,
14
+ trivialThresholds: {
15
+ maxACItems: 3,
16
+ maxDirectories: 1,
17
+ },
18
+ thresholds: {
19
+ featureCount: { yellow: 2, red: 3 },
20
+ acItems: { yellow: 6, red: 9 },
21
+ fileEstimate: { yellow: 8, red: 13 },
22
+ directorySpread: { yellow: 3, red: 5 },
23
+ },
24
+ };
25
+ /**
26
+ * Zod schema for scope assessment (for state storage)
27
+ */
28
+ export const ScopeMetricSchema = z.object({
29
+ name: z.string(),
30
+ value: z.number(),
31
+ status: z.enum(["green", "yellow", "red"]),
32
+ });
33
+ export const ACClusterSchema = z.object({
34
+ keyword: z.string(),
35
+ acIds: z.array(z.string()),
36
+ count: z.number(),
37
+ });
38
+ export const FeatureDetectionSchema = z.object({
39
+ featureCount: z.number(),
40
+ clusters: z.array(ACClusterSchema),
41
+ multipleVerbs: z.boolean(),
42
+ titleVerbs: z.array(z.string()),
43
+ directorySpread: z.number(),
44
+ directories: z.array(z.string()),
45
+ });
46
+ export const NonGoalsSchema = z.object({
47
+ items: z.array(z.string()),
48
+ found: z.boolean(),
49
+ warning: z.string().optional(),
50
+ });
51
+ export const ScopeAssessmentSchema = z.object({
52
+ assessedAt: z.string().datetime(),
53
+ skipped: z.boolean(),
54
+ skipReason: z.string().optional(),
55
+ verdict: z.enum(["SCOPE_OK", "SCOPE_WARNING", "SCOPE_SPLIT_RECOMMENDED"]),
56
+ metrics: z.array(ScopeMetricSchema),
57
+ featureDetection: FeatureDetectionSchema,
58
+ nonGoals: NonGoalsSchema,
59
+ recommendation: z.string(),
60
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Scope Verdict Calculator
3
+ *
4
+ * Determines scope verdict based on metrics and thresholds.
5
+ * Provides recommendations for scope improvement.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { calculateVerdict, getMetricStatus } from './verdict';
10
+ *
11
+ * const metrics = [
12
+ * { name: 'featureCount', value: 2, status: 'yellow' },
13
+ * { name: 'acItems', value: 8, status: 'yellow' },
14
+ * ];
15
+ * const verdict = calculateVerdict(metrics);
16
+ * // Returns: 'SCOPE_WARNING'
17
+ * ```
18
+ */
19
+ import type { ScopeMetric, ScopeMetricStatus, ScopeVerdict, ScopeAssessmentConfig, MetricThreshold, FeatureDetection, NonGoals } from "./types.js";
20
+ /**
21
+ * Get status for a metric value based on thresholds
22
+ *
23
+ * @param value - Current metric value
24
+ * @param threshold - Threshold configuration
25
+ * @returns Metric status (green/yellow/red)
26
+ */
27
+ export declare function getMetricStatus(value: number, threshold: MetricThreshold): ScopeMetricStatus;
28
+ /**
29
+ * Create scope metrics from feature detection result
30
+ *
31
+ * @param detection - Feature detection result
32
+ * @param acCount - Total AC items count
33
+ * @param config - Scope assessment configuration
34
+ * @returns Array of scope metrics with status
35
+ */
36
+ export declare function createScopeMetrics(detection: FeatureDetection, acCount: number, config?: ScopeAssessmentConfig): ScopeMetric[];
37
+ /**
38
+ * Calculate overall verdict from metrics
39
+ *
40
+ * Logic:
41
+ * - Any red metric → SCOPE_SPLIT_RECOMMENDED
42
+ * - Any yellow metric → SCOPE_WARNING
43
+ * - All green → SCOPE_OK
44
+ *
45
+ * @param metrics - Array of scope metrics
46
+ * @param nonGoals - Non-goals parsing result
47
+ * @returns Overall scope verdict
48
+ */
49
+ export declare function calculateVerdict(metrics: ScopeMetric[], nonGoals: NonGoals): ScopeVerdict;
50
+ /**
51
+ * Generate recommendation message based on verdict and metrics
52
+ *
53
+ * @param verdict - Calculated verdict
54
+ * @param metrics - Scope metrics
55
+ * @param detection - Feature detection result
56
+ * @param nonGoals - Non-goals parsing result
57
+ * @returns Recommendation message
58
+ */
59
+ export declare function generateRecommendation(verdict: ScopeVerdict, metrics: ScopeMetric[], detection: FeatureDetection, nonGoals: NonGoals): string;
60
+ /**
61
+ * Get emoji indicator for verdict
62
+ *
63
+ * @param verdict - Scope verdict
64
+ * @returns Emoji string
65
+ */
66
+ export declare function getVerdictEmoji(verdict: ScopeVerdict): string;
67
+ /**
68
+ * Get emoji indicator for metric status
69
+ *
70
+ * @param status - Metric status
71
+ * @returns Emoji string
72
+ */
73
+ export declare function getStatusEmoji(status: ScopeMetricStatus): string;
74
+ /**
75
+ * Check if quality loop should be auto-enabled based on verdict
76
+ *
77
+ * @param verdict - Scope verdict
78
+ * @returns Whether to enable quality loop
79
+ */
80
+ export declare function shouldEnableQualityLoop(verdict: ScopeVerdict): boolean;