sequant 1.11.0 → 1.13.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.
Files changed (66) hide show
  1. package/README.md +93 -7
  2. package/dist/bin/cli.js +12 -9
  3. package/dist/src/commands/doctor.js +25 -20
  4. package/dist/src/commands/init.js +152 -65
  5. package/dist/src/commands/logs.js +7 -6
  6. package/dist/src/commands/run.d.ts +13 -1
  7. package/dist/src/commands/run.js +75 -12
  8. package/dist/src/commands/stats.js +67 -48
  9. package/dist/src/commands/status.js +30 -12
  10. package/dist/src/index.d.ts +6 -0
  11. package/dist/src/index.js +4 -0
  12. package/dist/src/lib/ac-linter.d.ts +116 -0
  13. package/dist/src/lib/ac-linter.js +304 -0
  14. package/dist/src/lib/cli-ui.d.ts +196 -0
  15. package/dist/src/lib/cli-ui.js +544 -0
  16. package/dist/src/lib/content-analyzer.d.ts +89 -0
  17. package/dist/src/lib/content-analyzer.js +437 -0
  18. package/dist/src/lib/phase-signal.d.ts +94 -0
  19. package/dist/src/lib/phase-signal.js +171 -0
  20. package/dist/src/lib/plugin-version-sync.d.ts +26 -0
  21. package/dist/src/lib/plugin-version-sync.js +91 -0
  22. package/dist/src/lib/project-name.d.ts +40 -0
  23. package/dist/src/lib/project-name.js +191 -0
  24. package/dist/src/lib/semgrep.d.ts +136 -0
  25. package/dist/src/lib/semgrep.js +406 -0
  26. package/dist/src/lib/solve-comment-parser.d.ts +84 -0
  27. package/dist/src/lib/solve-comment-parser.js +200 -0
  28. package/dist/src/lib/stack-config.d.ts +51 -0
  29. package/dist/src/lib/stack-config.js +77 -0
  30. package/dist/src/lib/stacks.d.ts +66 -0
  31. package/dist/src/lib/stacks.js +332 -0
  32. package/dist/src/lib/templates.d.ts +2 -0
  33. package/dist/src/lib/templates.js +12 -3
  34. package/dist/src/lib/upstream/assessment.d.ts +70 -0
  35. package/dist/src/lib/upstream/assessment.js +385 -0
  36. package/dist/src/lib/upstream/index.d.ts +11 -0
  37. package/dist/src/lib/upstream/index.js +14 -0
  38. package/dist/src/lib/upstream/issues.d.ts +38 -0
  39. package/dist/src/lib/upstream/issues.js +267 -0
  40. package/dist/src/lib/upstream/relevance.d.ts +50 -0
  41. package/dist/src/lib/upstream/relevance.js +209 -0
  42. package/dist/src/lib/upstream/report.d.ts +29 -0
  43. package/dist/src/lib/upstream/report.js +391 -0
  44. package/dist/src/lib/upstream/types.d.ts +207 -0
  45. package/dist/src/lib/upstream/types.js +5 -0
  46. package/dist/src/lib/workflow/log-writer.d.ts +1 -1
  47. package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
  48. package/dist/src/lib/workflow/qa-cache.d.ts +199 -0
  49. package/dist/src/lib/workflow/qa-cache.js +440 -0
  50. package/dist/src/lib/workflow/run-log-schema.d.ts +34 -6
  51. package/dist/src/lib/workflow/run-log-schema.js +12 -1
  52. package/dist/src/lib/workflow/state-schema.d.ts +4 -4
  53. package/dist/src/lib/workflow/types.d.ts +4 -0
  54. package/package.json +6 -1
  55. package/templates/hooks/pre-tool.sh +6 -0
  56. package/templates/memory/constitution.md +1 -5
  57. package/templates/skills/_shared/references/prompt-templates.md +350 -0
  58. package/templates/skills/_shared/references/subagent-types.md +131 -0
  59. package/templates/skills/exec/SKILL.md +82 -0
  60. package/templates/skills/fullsolve/SKILL.md +19 -2
  61. package/templates/skills/loop/SKILL.md +3 -1
  62. package/templates/skills/qa/SKILL.md +79 -9
  63. package/templates/skills/qa/references/quality-gates.md +85 -1
  64. package/templates/skills/qa/references/semgrep-rules.md +207 -0
  65. package/templates/skills/qa/scripts/quality-checks.sh +525 -15
  66. package/templates/skills/spec/SKILL.md +322 -9
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Acceptance Criteria Linter
3
+ *
4
+ * Static analysis of acceptance criteria to flag vague, untestable,
5
+ * or incomplete requirements before implementation begins.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { lintAcceptanceCriteria } from './ac-linter';
10
+ * import { parseAcceptanceCriteria } from './ac-parser';
11
+ *
12
+ * const criteria = parseAcceptanceCriteria(issueBody);
13
+ * const lintResults = lintAcceptanceCriteria(criteria);
14
+ *
15
+ * console.log(formatACLintResults(lintResults));
16
+ * ```
17
+ */
18
+ /**
19
+ * Default lint patterns organized by issue type
20
+ *
21
+ * Patterns are checked in order, with longer/more specific patterns first
22
+ */
23
+ const DEFAULT_LINT_PATTERNS = [
24
+ // Vague patterns
25
+ {
26
+ regex: /\bshould work\b/i,
27
+ type: "vague",
28
+ problem: 'Vague: "should work" is not specific',
29
+ suggestion: "Specify the expected behavior and success criteria",
30
+ },
31
+ {
32
+ regex: /\bwork(?:s|ing)? (?:properly|correctly|well|nicely)\b/i,
33
+ type: "vague",
34
+ problem: "Vague: adverb does not define expected behavior",
35
+ suggestion: "Define specific, measurable outcomes",
36
+ },
37
+ {
38
+ regex: /\bproperly\b/i,
39
+ type: "vague",
40
+ problem: 'Vague: "properly" is subjective',
41
+ suggestion: "Specify what correct behavior looks like",
42
+ },
43
+ {
44
+ regex: /\bcorrectly\b/i,
45
+ type: "vague",
46
+ problem: 'Vague: "correctly" is subjective',
47
+ suggestion: "Define the expected output or behavior",
48
+ },
49
+ {
50
+ regex: /\bnicely\b/i,
51
+ type: "vague",
52
+ problem: 'Vague: "nicely" is subjective',
53
+ suggestion: "Specify concrete UX requirements",
54
+ },
55
+ {
56
+ regex: /\bgood\b(?!\s+(?:practice|pattern|reason))/i,
57
+ type: "vague",
58
+ problem: 'Vague: "good" is subjective without context',
59
+ suggestion: "Define measurable quality criteria",
60
+ },
61
+ {
62
+ regex: /\bas expected\b/i,
63
+ type: "vague",
64
+ problem: 'Vague: "as expected" requires explicit expectations',
65
+ suggestion: "Define what the expected behavior is",
66
+ },
67
+ {
68
+ regex: /\bshould be fine\b/i,
69
+ type: "vague",
70
+ problem: 'Vague: "should be fine" is not a testable criterion',
71
+ suggestion: "Specify the acceptance threshold",
72
+ },
73
+ // Unmeasurable patterns (performance-related)
74
+ {
75
+ regex: /\bfast(?:er)?\b(?!\s+forward)/i,
76
+ type: "unmeasurable",
77
+ problem: 'Unmeasurable: "fast" has no threshold',
78
+ suggestion: "Add latency threshold (e.g., <2 seconds, <100ms)",
79
+ },
80
+ {
81
+ regex: /\bslow(?:er)?\b/i,
82
+ type: "unmeasurable",
83
+ problem: 'Unmeasurable: "slow" has no threshold',
84
+ suggestion: "Define specific timing or threshold",
85
+ },
86
+ {
87
+ regex: /\bperformant\b/i,
88
+ type: "unmeasurable",
89
+ problem: 'Unmeasurable: "performant" has no threshold',
90
+ suggestion: "Add specific performance metrics (latency, throughput)",
91
+ },
92
+ {
93
+ regex: /\befficient(?:ly)?\b/i,
94
+ type: "unmeasurable",
95
+ problem: 'Unmeasurable: "efficient" has no threshold',
96
+ suggestion: "Define resource usage limits or benchmarks",
97
+ },
98
+ {
99
+ regex: /\bresponsive\b/i,
100
+ type: "unmeasurable",
101
+ problem: 'Unmeasurable: "responsive" has no threshold',
102
+ suggestion: "Add response time target (e.g., <100ms interaction, <3s load)",
103
+ },
104
+ {
105
+ regex: /\bquick(?:ly)?\b/i,
106
+ type: "unmeasurable",
107
+ problem: 'Unmeasurable: "quickly" has no threshold',
108
+ suggestion: "Specify time limit (e.g., completes in <5 seconds)",
109
+ },
110
+ {
111
+ regex: /\bscalable\b/i,
112
+ type: "unmeasurable",
113
+ problem: 'Unmeasurable: "scalable" needs concrete bounds',
114
+ suggestion: "Define load targets (e.g., handles 1000 concurrent users)",
115
+ },
116
+ // Incomplete patterns (error handling, edge cases)
117
+ {
118
+ regex: /\bhandle(?:s)? errors?\b/i,
119
+ type: "incomplete",
120
+ problem: "Incomplete: error types not specified",
121
+ suggestion: "List specific error types and expected responses (e.g., 400 for invalid input, 503 for service unavailable)",
122
+ },
123
+ {
124
+ regex: /\berror handling\b/i,
125
+ type: "incomplete",
126
+ problem: "Incomplete: error scenarios not specified",
127
+ suggestion: "Enumerate error conditions and recovery behaviors",
128
+ },
129
+ {
130
+ regex: /\bedge cases?\b/i,
131
+ type: "incomplete",
132
+ problem: "Incomplete: edge cases not enumerated",
133
+ suggestion: "List the specific edge cases to handle",
134
+ },
135
+ {
136
+ regex: /\bcorner cases?\b/i,
137
+ type: "incomplete",
138
+ problem: "Incomplete: corner cases not enumerated",
139
+ suggestion: "List the specific corner cases to handle",
140
+ },
141
+ {
142
+ regex: /\ball (?:cases|scenarios|situations)\b/i,
143
+ type: "incomplete",
144
+ problem: 'Incomplete: "all" cases cannot be verified',
145
+ suggestion: "Enumerate the specific cases to test",
146
+ },
147
+ {
148
+ regex: /\bappropriate(?:ly)?\b/i,
149
+ type: "incomplete",
150
+ problem: 'Incomplete: "appropriate" behavior not defined',
151
+ suggestion: "Specify what the appropriate response is",
152
+ },
153
+ // Open-ended patterns
154
+ {
155
+ regex: /\betc\.?\b/i,
156
+ type: "open_ended",
157
+ problem: 'Open-ended: "etc." leaves scope undefined',
158
+ suggestion: "Enumerate all items explicitly",
159
+ },
160
+ {
161
+ regex: /\band more\b/i,
162
+ type: "open_ended",
163
+ problem: 'Open-ended: "and more" leaves scope undefined',
164
+ suggestion: "List all items explicitly",
165
+ },
166
+ {
167
+ regex: /\bsuch as\b/i,
168
+ type: "open_ended",
169
+ problem: 'Open-ended: "such as" implies incomplete list',
170
+ suggestion: "Provide exhaustive list or define boundaries",
171
+ },
172
+ {
173
+ regex: /\bincluding but not limited to\b/i,
174
+ type: "open_ended",
175
+ problem: "Open-ended: scope is unbounded",
176
+ suggestion: "Define explicit boundaries or enumerate all items",
177
+ },
178
+ {
179
+ regex: /\bfor example\b/i,
180
+ type: "open_ended",
181
+ problem: 'Open-ended: "for example" implies other cases exist',
182
+ suggestion: "List all cases or define scope boundaries",
183
+ },
184
+ {
185
+ regex: /\bvarious\b/i,
186
+ type: "open_ended",
187
+ problem: 'Open-ended: "various" items not specified',
188
+ suggestion: "Enumerate the specific items",
189
+ },
190
+ {
191
+ regex: /\bother(?:s)?\b(?!\s+(?:than|hand|words|side))/i,
192
+ type: "open_ended",
193
+ problem: 'Open-ended: "other" items not specified',
194
+ suggestion: "List all items explicitly or define boundaries",
195
+ },
196
+ ];
197
+ /**
198
+ * Lint a single acceptance criterion against all patterns
199
+ *
200
+ * @param ac - The acceptance criterion to lint
201
+ * @param patterns - Optional custom patterns (defaults to DEFAULT_LINT_PATTERNS)
202
+ * @returns Lint result with any issues found
203
+ */
204
+ export function lintAcceptanceCriterion(ac, patterns = DEFAULT_LINT_PATTERNS) {
205
+ const issues = [];
206
+ const description = ac.description;
207
+ for (const pattern of patterns) {
208
+ const match = description.match(pattern.regex);
209
+ if (match) {
210
+ issues.push({
211
+ type: pattern.type,
212
+ matchedPattern: match[0],
213
+ problem: pattern.problem,
214
+ suggestion: pattern.suggestion,
215
+ });
216
+ }
217
+ }
218
+ return {
219
+ ac,
220
+ issues,
221
+ passed: issues.length === 0,
222
+ };
223
+ }
224
+ /**
225
+ * Lint all acceptance criteria
226
+ *
227
+ * @param criteria - Array of acceptance criteria to lint
228
+ * @param patterns - Optional custom patterns
229
+ * @returns Complete lint results with summary
230
+ */
231
+ export function lintAcceptanceCriteria(criteria, patterns) {
232
+ const results = criteria.map((ac) => lintAcceptanceCriterion(ac, patterns));
233
+ const passed = results.filter((r) => r.passed).length;
234
+ const flagged = results.filter((r) => !r.passed).length;
235
+ return {
236
+ results,
237
+ summary: {
238
+ total: criteria.length,
239
+ passed,
240
+ flagged,
241
+ },
242
+ hasIssues: flagged > 0,
243
+ };
244
+ }
245
+ /**
246
+ * Format lint results as markdown for the spec output
247
+ *
248
+ * @param results - Lint results to format
249
+ * @returns Markdown-formatted output string
250
+ */
251
+ export function formatACLintResults(results) {
252
+ if (results.summary.total === 0) {
253
+ return "## AC Quality Check\n\nNo acceptance criteria found to lint.";
254
+ }
255
+ const lines = ["## AC Quality Check", ""];
256
+ // Add summary line
257
+ if (!results.hasIssues) {
258
+ lines.push(`✅ All ${results.summary.total} acceptance criteria are clear and testable.`);
259
+ lines.push("");
260
+ return lines.join("\n");
261
+ }
262
+ // Add issues
263
+ for (const result of results.results) {
264
+ if (!result.passed) {
265
+ lines.push(`⚠️ **${result.ac.id}:** "${result.ac.description}"`);
266
+ for (const issue of result.issues) {
267
+ lines.push(` → ${issue.problem}`);
268
+ lines.push(` → Suggest: ${issue.suggestion}`);
269
+ }
270
+ lines.push("");
271
+ }
272
+ }
273
+ // Add passed ACs summary
274
+ const passedIds = results.results.filter((r) => r.passed).map((r) => r.ac.id);
275
+ if (passedIds.length > 0) {
276
+ lines.push(`✅ ${passedIds.join(", ")}: Clear and testable`);
277
+ lines.push("");
278
+ }
279
+ // Add summary
280
+ lines.push(`**Summary:** ${results.summary.flagged}/${results.summary.total} AC items flagged for review`);
281
+ return lines.join("\n");
282
+ }
283
+ /**
284
+ * Get the default lint patterns
285
+ *
286
+ * @returns Copy of the default lint patterns
287
+ */
288
+ export function getDefaultLintPatterns() {
289
+ return [...DEFAULT_LINT_PATTERNS];
290
+ }
291
+ /**
292
+ * Create custom lint patterns from a simplified configuration
293
+ *
294
+ * @param config - Array of pattern configurations
295
+ * @returns Array of LintPattern objects
296
+ */
297
+ export function createLintPatterns(config) {
298
+ return config.map((c) => ({
299
+ regex: new RegExp(c.pattern, "i"),
300
+ type: c.type,
301
+ problem: c.problem,
302
+ suggestion: c.suggestion,
303
+ }));
304
+ }
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Centralized CLI UI module for Sequant
3
+ *
4
+ * Provides consistent styling, spinners, boxes, tables, and branding
5
+ * with graceful fallbacks for non-TTY, CI, and legacy terminals.
6
+ */
7
+ /**
8
+ * UI configuration options
9
+ */
10
+ export interface UIConfig {
11
+ /** Disable all colors and styling */
12
+ noColor: boolean;
13
+ /** JSON output mode - suppress decorative output */
14
+ jsonMode: boolean;
15
+ /** Verbose mode - use text-only spinners to avoid overwriting output */
16
+ verbose: boolean;
17
+ /** Whether stdout is a TTY */
18
+ isTTY: boolean;
19
+ /** Running in CI environment */
20
+ isCI: boolean;
21
+ /** Minimal output mode */
22
+ minimal: boolean;
23
+ }
24
+ /**
25
+ * Configure UI settings
26
+ *
27
+ * Call this early in CLI startup to set global options.
28
+ */
29
+ export declare function configureUI(options: Partial<UIConfig>): void;
30
+ /**
31
+ * Get current UI configuration
32
+ */
33
+ export declare function getUIConfig(): Readonly<UIConfig>;
34
+ /**
35
+ * Standardized color palette for consistent styling
36
+ */
37
+ export declare const colors: {
38
+ success: ((s: string) => string) | import("chalk").ChalkInstance;
39
+ error: import("chalk").ChalkInstance | ((s: string) => string);
40
+ warning: import("chalk").ChalkInstance | ((s: string) => string);
41
+ info: import("chalk").ChalkInstance | ((s: string) => string);
42
+ muted: import("chalk").ChalkInstance | ((s: string) => string);
43
+ header: import("chalk").ChalkInstance | ((s: string) => string);
44
+ label: import("chalk").ChalkInstance | ((s: string) => string);
45
+ value: import("chalk").ChalkInstance | ((s: string) => string);
46
+ accent: import("chalk").ChalkInstance | ((s: string) => string);
47
+ bold: import("chalk").ChalkInstance | ((s: string) => string);
48
+ pending: import("chalk").ChalkInstance | ((s: string) => string);
49
+ running: import("chalk").ChalkInstance | ((s: string) => string);
50
+ completed: import("chalk").ChalkInstance | ((s: string) => string);
51
+ failed: import("chalk").ChalkInstance | ((s: string) => string);
52
+ };
53
+ /**
54
+ * Get the ASCII logo with optional gradient
55
+ */
56
+ export declare function logo(): string;
57
+ /**
58
+ * Get the banner (logo + tagline)
59
+ */
60
+ export declare function banner(): string;
61
+ /**
62
+ * Spinner manager interface
63
+ */
64
+ export interface SpinnerManager {
65
+ /** Start the spinner with optional new text */
66
+ start(text?: string): SpinnerManager;
67
+ /** Mark as succeeded with optional message */
68
+ succeed(text?: string): SpinnerManager;
69
+ /** Mark as failed with optional message */
70
+ fail(text?: string): SpinnerManager;
71
+ /** Mark as warning with optional message */
72
+ warn(text?: string): SpinnerManager;
73
+ /** Stop the spinner */
74
+ stop(): SpinnerManager;
75
+ /** Update the spinner text */
76
+ text: string;
77
+ /** Check if spinner is active */
78
+ isSpinning: boolean;
79
+ }
80
+ /**
81
+ * Create a spinner with automatic fallback
82
+ *
83
+ * Returns an animated spinner for TTY, text-only for non-TTY/verbose/CI.
84
+ */
85
+ export declare function spinner(text: string): SpinnerManager;
86
+ /**
87
+ * Box style presets
88
+ */
89
+ export type BoxStyle = "success" | "error" | "warning" | "info" | "header" | "default";
90
+ /**
91
+ * Create a boxed message
92
+ */
93
+ export declare function box(content: string, style?: BoxStyle): string;
94
+ /**
95
+ * Create a success box with title and message
96
+ */
97
+ export declare function successBox(title: string, message: string): string;
98
+ /**
99
+ * Create an error box with title and message
100
+ */
101
+ export declare function errorBox(title: string, message: string): string;
102
+ /**
103
+ * Create a warning box with title and message
104
+ */
105
+ export declare function warningBox(title: string, message: string): string;
106
+ /**
107
+ * Create a header box
108
+ */
109
+ export declare function headerBox(title: string): string;
110
+ /**
111
+ * Table column definition
112
+ */
113
+ export interface TableColumn {
114
+ /** Column header text */
115
+ header: string;
116
+ /** Column width (optional) */
117
+ width?: number;
118
+ /** Text alignment */
119
+ align?: "left" | "center" | "right";
120
+ }
121
+ /**
122
+ * Table options
123
+ */
124
+ export interface TableOptions {
125
+ /** Column definitions */
126
+ columns: TableColumn[];
127
+ /** Table style */
128
+ style?: "default" | "compact" | "borderless";
129
+ }
130
+ /**
131
+ * Create a formatted table
132
+ */
133
+ export declare function table(rows: (string | number)[][], options: TableOptions): string;
134
+ /**
135
+ * Create a simple key-value table
136
+ */
137
+ export declare function keyValueTable(data: Record<string, string | number>): string;
138
+ /**
139
+ * Status indicator types
140
+ */
141
+ export type StatusType = "success" | "error" | "warning" | "pending" | "running";
142
+ /**
143
+ * Get a status icon
144
+ */
145
+ export declare function statusIcon(type: StatusType): string;
146
+ /**
147
+ * Print a status message with icon
148
+ */
149
+ export declare function printStatus(type: StatusType, message: string): void;
150
+ /**
151
+ * Create a horizontal divider
152
+ */
153
+ export declare function divider(width?: number): string;
154
+ /**
155
+ * Create a section header
156
+ */
157
+ export declare function sectionHeader(title: string): string;
158
+ /**
159
+ * Phase status for progress display
160
+ */
161
+ export interface PhaseStatus {
162
+ name: string;
163
+ status: "pending" | "running" | "success" | "failure" | "skipped";
164
+ }
165
+ /**
166
+ * Display phase progress as a visual indicator
167
+ */
168
+ export declare function phaseProgress(phases: PhaseStatus[]): string;
169
+ /**
170
+ * Create a progress bar
171
+ */
172
+ export declare function progressBar(current: number, total: number, width?: number): string;
173
+ /**
174
+ * Unified UI object for convenient access to all utilities
175
+ */
176
+ export declare const ui: {
177
+ configure: typeof configureUI;
178
+ getConfig: typeof getUIConfig;
179
+ logo: typeof logo;
180
+ banner: typeof banner;
181
+ spinner: typeof spinner;
182
+ box: typeof box;
183
+ successBox: typeof successBox;
184
+ errorBox: typeof errorBox;
185
+ warningBox: typeof warningBox;
186
+ headerBox: typeof headerBox;
187
+ table: typeof table;
188
+ keyValueTable: typeof keyValueTable;
189
+ statusIcon: typeof statusIcon;
190
+ printStatus: typeof printStatus;
191
+ divider: typeof divider;
192
+ sectionHeader: typeof sectionHeader;
193
+ phaseProgress: typeof phaseProgress;
194
+ progressBar: typeof progressBar;
195
+ };
196
+ export default ui;