sequant 1.20.3 → 2.0.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 (137) hide show
  1. package/.claude-plugin/marketplace.json +2 -4
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +36 -15
  4. package/dist/bin/cli.js +25 -2
  5. package/dist/src/commands/doctor.js +42 -9
  6. package/dist/src/commands/init.d.ts +1 -0
  7. package/dist/src/commands/init.js +52 -0
  8. package/dist/src/commands/logs.d.ts +1 -0
  9. package/dist/src/commands/logs.js +18 -2
  10. package/dist/src/commands/run.d.ts +7 -0
  11. package/dist/src/commands/run.js +235 -68
  12. package/dist/src/commands/serve.d.ts +13 -0
  13. package/dist/src/commands/serve.js +131 -0
  14. package/dist/src/commands/stats.d.ts +1 -0
  15. package/dist/src/commands/stats.js +185 -26
  16. package/dist/src/commands/status.d.ts +2 -0
  17. package/dist/src/commands/status.js +99 -50
  18. package/dist/src/index.d.ts +2 -2
  19. package/dist/src/index.js +4 -1
  20. package/dist/src/lib/ac-parser.d.ts +2 -0
  21. package/dist/src/lib/ac-parser.js +12 -2
  22. package/dist/src/lib/assess-comment-parser.d.ts +137 -0
  23. package/dist/src/lib/assess-comment-parser.js +344 -0
  24. package/dist/src/lib/ci/config.d.ts +22 -0
  25. package/dist/src/lib/ci/config.js +134 -0
  26. package/dist/src/lib/ci/index.d.ts +12 -0
  27. package/dist/src/lib/ci/index.js +10 -0
  28. package/dist/src/lib/ci/inputs.d.ts +29 -0
  29. package/dist/src/lib/ci/inputs.js +103 -0
  30. package/dist/src/lib/ci/labels.d.ts +34 -0
  31. package/dist/src/lib/ci/labels.js +101 -0
  32. package/dist/src/lib/ci/outputs.d.ts +25 -0
  33. package/dist/src/lib/ci/outputs.js +84 -0
  34. package/dist/src/lib/ci/triggers.d.ts +9 -0
  35. package/dist/src/lib/ci/triggers.js +86 -0
  36. package/dist/src/lib/ci/types.d.ts +131 -0
  37. package/dist/src/lib/ci/types.js +47 -0
  38. package/dist/src/lib/mcp-config.d.ts +54 -0
  39. package/dist/src/lib/mcp-config.js +172 -0
  40. package/dist/src/lib/merge-check/index.js +6 -12
  41. package/dist/src/lib/merge-check/types.d.ts +20 -7
  42. package/dist/src/lib/merge-check/types.js +11 -0
  43. package/dist/src/lib/phase-signal.d.ts +3 -3
  44. package/dist/src/lib/phase-signal.js +5 -3
  45. package/dist/src/lib/settings.d.ts +52 -0
  46. package/dist/src/lib/settings.js +41 -0
  47. package/dist/src/lib/shutdown.d.ts +16 -5
  48. package/dist/src/lib/shutdown.js +32 -12
  49. package/dist/src/lib/solve-comment-parser.d.ts +9 -102
  50. package/dist/src/lib/solve-comment-parser.js +13 -248
  51. package/dist/src/lib/stacks.d.ts +8 -0
  52. package/dist/src/lib/stacks.js +34 -0
  53. package/dist/src/lib/system.js +3 -7
  54. package/dist/src/lib/test-tautology-detector.d.ts +10 -0
  55. package/dist/src/lib/test-tautology-detector.js +43 -4
  56. package/dist/src/lib/upstream/assessment.js +9 -59
  57. package/dist/src/lib/upstream/issues.js +12 -75
  58. package/dist/src/lib/version-check.d.ts +2 -2
  59. package/dist/src/lib/version-check.js +6 -3
  60. package/dist/src/lib/version.d.ts +4 -0
  61. package/dist/src/lib/version.js +25 -0
  62. package/dist/src/lib/workflow/batch-executor.d.ts +26 -86
  63. package/dist/src/lib/workflow/batch-executor.js +269 -55
  64. package/dist/src/lib/workflow/drivers/agent-driver.d.ts +56 -0
  65. package/dist/src/lib/workflow/drivers/agent-driver.js +8 -0
  66. package/dist/src/lib/workflow/drivers/aider.d.ts +18 -0
  67. package/dist/src/lib/workflow/drivers/aider.js +160 -0
  68. package/dist/src/lib/workflow/drivers/claude-code.d.ts +17 -0
  69. package/dist/src/lib/workflow/drivers/claude-code.js +165 -0
  70. package/dist/src/lib/workflow/drivers/index.d.ts +20 -0
  71. package/dist/src/lib/workflow/drivers/index.js +27 -0
  72. package/dist/src/lib/workflow/error-classifier.d.ts +16 -0
  73. package/dist/src/lib/workflow/error-classifier.js +90 -0
  74. package/dist/src/lib/workflow/log-writer.d.ts +6 -3
  75. package/dist/src/lib/workflow/log-writer.js +57 -27
  76. package/dist/src/lib/workflow/metrics-schema.d.ts +9 -9
  77. package/dist/src/lib/workflow/phase-detection.d.ts +23 -0
  78. package/dist/src/lib/workflow/phase-detection.js +45 -29
  79. package/dist/src/lib/workflow/phase-executor.d.ts +42 -3
  80. package/dist/src/lib/workflow/phase-executor.js +375 -229
  81. package/dist/src/lib/workflow/phase-mapper.d.ts +1 -1
  82. package/dist/src/lib/workflow/phase-mapper.js +7 -7
  83. package/dist/src/lib/workflow/platforms/github.d.ts +157 -0
  84. package/dist/src/lib/workflow/platforms/github.js +466 -0
  85. package/dist/src/lib/workflow/platforms/index.d.ts +17 -0
  86. package/dist/src/lib/workflow/platforms/index.js +25 -0
  87. package/dist/src/lib/workflow/platforms/platform-provider.d.ts +67 -0
  88. package/dist/src/lib/workflow/platforms/platform-provider.js +8 -0
  89. package/dist/src/lib/workflow/pr-status.d.ts +2 -4
  90. package/dist/src/lib/workflow/pr-status.js +3 -16
  91. package/dist/src/lib/workflow/qa-cache.d.ts +58 -0
  92. package/dist/src/lib/workflow/qa-cache.js +88 -0
  93. package/dist/src/lib/workflow/reconcile.d.ts +69 -0
  94. package/dist/src/lib/workflow/reconcile.js +290 -0
  95. package/dist/src/lib/workflow/ring-buffer.d.ts +17 -0
  96. package/dist/src/lib/workflow/ring-buffer.js +37 -0
  97. package/dist/src/lib/workflow/run-log-schema.d.ts +115 -24
  98. package/dist/src/lib/workflow/run-log-schema.js +47 -12
  99. package/dist/src/lib/workflow/run-reflect.js +1 -1
  100. package/dist/src/lib/workflow/state-cleanup.js +21 -0
  101. package/dist/src/lib/workflow/state-manager.d.ts +34 -3
  102. package/dist/src/lib/workflow/state-manager.js +278 -126
  103. package/dist/src/lib/workflow/state-schema.d.ts +34 -30
  104. package/dist/src/lib/workflow/state-schema.js +35 -25
  105. package/dist/src/lib/workflow/state-utils.d.ts +3 -1
  106. package/dist/src/lib/workflow/state-utils.js +1 -0
  107. package/dist/src/lib/workflow/types.d.ts +224 -6
  108. package/dist/src/lib/workflow/types.js +20 -1
  109. package/dist/src/lib/workflow/worktree-discovery.d.ts +1 -1
  110. package/dist/src/lib/workflow/worktree-discovery.js +6 -14
  111. package/dist/src/lib/workflow/worktree-manager.js +33 -51
  112. package/dist/src/mcp/index.d.ts +4 -0
  113. package/dist/src/mcp/index.js +4 -0
  114. package/dist/src/mcp/resources.d.ts +7 -0
  115. package/dist/src/mcp/resources.js +111 -0
  116. package/dist/src/mcp/run-registry.d.ts +34 -0
  117. package/dist/src/mcp/run-registry.js +42 -0
  118. package/dist/src/mcp/server.d.ts +12 -0
  119. package/dist/src/mcp/server.js +50 -0
  120. package/dist/src/mcp/tools/logs.d.ts +7 -0
  121. package/dist/src/mcp/tools/logs.js +149 -0
  122. package/dist/src/mcp/tools/run.d.ts +121 -0
  123. package/dist/src/mcp/tools/run.js +591 -0
  124. package/dist/src/mcp/tools/status.d.ts +7 -0
  125. package/dist/src/mcp/tools/status.js +127 -0
  126. package/package.json +26 -7
  127. package/templates/hooks/post-tool.sh +19 -8
  128. package/templates/hooks/pre-tool.sh +36 -49
  129. package/templates/mcp.json +6 -0
  130. package/templates/skills/assess/SKILL.md +354 -352
  131. package/templates/skills/exec/SKILL.md +64 -1
  132. package/templates/skills/fullsolve/SKILL.md +35 -4
  133. package/templates/skills/qa/SKILL.md +486 -9
  134. package/templates/skills/qa/scripts/quality-checks.sh +1 -1
  135. package/templates/skills/setup/SKILL.md +386 -0
  136. package/templates/skills/solve/SKILL.md +38 -664
  137. package/templates/skills/spec/SKILL.md +90 -31
@@ -1,110 +1,17 @@
1
1
  /**
2
- * Solve Comment Parser
2
+ * Solve Comment Parser — Backward Compatibility Re-exports
3
3
  *
4
- * Detects and parses /solve command output from GitHub issue comments.
5
- * When a solve comment exists, its phase recommendations take precedence
6
- * over content analysis (but not over labels).
4
+ * @deprecated This module is deprecated. Use `assess-comment-parser` instead.
5
+ * All exports are re-exported from the unified assess-comment-parser module.
7
6
  *
8
- * @example
7
+ * Migration:
9
8
  * ```typescript
9
+ * // Before (deprecated):
10
10
  * import { findSolveComment, parseSolveWorkflow } from './solve-comment-parser';
11
11
  *
12
- * const comments = [
13
- * { body: "Some regular comment" },
14
- * { body: "## Solve Workflow for Issues: 123\n..." },
15
- * ];
16
- *
17
- * const solveComment = findSolveComment(comments);
18
- * if (solveComment) {
19
- * const phases = parseSolveWorkflow(solveComment.body);
20
- * // phases: { phases: ['spec', 'exec', 'test', 'qa'], qualityLoop: false }
21
- * }
12
+ * // After:
13
+ * import { findAssessComment, parseAssessWorkflow } from './assess-comment-parser';
22
14
  * ```
23
15
  */
24
- import type { Phase } from "./workflow/types.js";
25
- import type { PhaseSignal } from "./phase-signal.js";
26
- /**
27
- * Result of parsing a solve comment
28
- */
29
- export interface SolveWorkflowResult {
30
- /** Phases recommended by solve */
31
- phases: Phase[];
32
- /** Whether quality loop is recommended */
33
- qualityLoop: boolean;
34
- /** The issue numbers mentioned in the solve comment */
35
- issueNumbers: number[];
36
- /** Raw workflow string (e.g., "spec → exec → test → qa") */
37
- workflowString?: string;
38
- }
39
- /**
40
- * Comment structure (simplified from GitHub API)
41
- */
42
- export interface IssueComment {
43
- body: string;
44
- author?: {
45
- login: string;
46
- };
47
- createdAt?: string;
48
- }
49
- /**
50
- * Check if a comment is a solve command output
51
- *
52
- * @param body - The comment body
53
- * @returns True if this appears to be a solve comment
54
- */
55
- export declare function isSolveComment(body: string): boolean;
56
- /**
57
- * Find the most recent solve comment from a list of comments
58
- *
59
- * @param comments - Array of issue comments
60
- * @returns The solve comment if found, null otherwise
61
- */
62
- export declare function findSolveComment(comments: IssueComment[]): IssueComment | null;
63
- /**
64
- * Structured data extracted from HTML comment markers in solve comments
65
- */
66
- export interface SolveMarkers {
67
- /** Recommended phases (from <!-- solve:phases=... -->) */
68
- phases?: string[];
69
- /** Whether to skip spec (from <!-- solve:skip-spec=... -->) */
70
- skipSpec?: boolean;
71
- /** Whether browser testing is needed (from <!-- solve:browser-test=... -->) */
72
- browserTest?: boolean;
73
- /** Whether quality loop is recommended (from <!-- solve:quality-loop=... -->) */
74
- qualityLoop?: boolean;
75
- }
76
- /**
77
- * Parse HTML comment markers from a solve comment body
78
- *
79
- * Extracts structured data from markers like:
80
- * - `<!-- solve:phases=exec,qa -->`
81
- * - `<!-- solve:skip-spec=true -->`
82
- * - `<!-- solve:browser-test=false -->`
83
- * - `<!-- solve:quality-loop=true -->`
84
- *
85
- * @param body - The comment body
86
- * @returns Parsed markers
87
- */
88
- export declare function parseSolveMarkers(body: string): SolveMarkers;
89
- /**
90
- * Parse a solve comment to extract workflow information
91
- *
92
- * @param body - The solve comment body
93
- * @returns Parsed workflow result
94
- */
95
- export declare function parseSolveWorkflow(body: string): SolveWorkflowResult;
96
- /**
97
- * Convert solve workflow result to phase signals
98
- *
99
- * @param workflow - The parsed solve workflow
100
- * @returns Array of phase signals with 'solve' source
101
- */
102
- export declare function solveWorkflowToSignals(workflow: SolveWorkflowResult): PhaseSignal[];
103
- /**
104
- * Check if solve comment covers the current issue
105
- *
106
- * @param workflow - The parsed solve workflow
107
- * @param issueNumber - The current issue number
108
- * @returns True if the solve comment includes this issue
109
- */
110
- export declare function solveCoversIssue(workflow: SolveWorkflowResult, issueNumber: number): boolean;
16
+ export { isAssessComment, findAssessComment, parseAssessMarkers, parseAssessWorkflow, assessWorkflowToSignals, assessCoversIssue, isSolveComment, findSolveComment, parseSolveMarkers, parseSolveWorkflow, solveWorkflowToSignals, solveCoversIssue, } from "./assess-comment-parser.js";
17
+ export type { AssessWorkflowResult, AssessMarkers, AssessAction, SolveWorkflowResult, SolveMarkers, IssueComment, } from "./assess-comment-parser.js";
@@ -1,256 +1,21 @@
1
1
  /**
2
- * Solve Comment Parser
2
+ * Solve Comment Parser — Backward Compatibility Re-exports
3
3
  *
4
- * Detects and parses /solve command output from GitHub issue comments.
5
- * When a solve comment exists, its phase recommendations take precedence
6
- * over content analysis (but not over labels).
4
+ * @deprecated This module is deprecated. Use `assess-comment-parser` instead.
5
+ * All exports are re-exported from the unified assess-comment-parser module.
7
6
  *
8
- * @example
7
+ * Migration:
9
8
  * ```typescript
9
+ * // Before (deprecated):
10
10
  * import { findSolveComment, parseSolveWorkflow } from './solve-comment-parser';
11
11
  *
12
- * const comments = [
13
- * { body: "Some regular comment" },
14
- * { body: "## Solve Workflow for Issues: 123\n..." },
15
- * ];
16
- *
17
- * const solveComment = findSolveComment(comments);
18
- * if (solveComment) {
19
- * const phases = parseSolveWorkflow(solveComment.body);
20
- * // phases: { phases: ['spec', 'exec', 'test', 'qa'], qualityLoop: false }
21
- * }
12
+ * // After:
13
+ * import { findAssessComment, parseAssessWorkflow } from './assess-comment-parser';
22
14
  * ```
23
15
  */
24
- /**
25
- * Markers that indicate a solve comment
26
- */
27
- const SOLVE_MARKERS = [
28
- "## Solve Analysis",
29
- "## Solve Workflow for Issues:",
30
- "## Solve Workflow for Issue:",
31
- "### Recommended Workflow",
32
- "*📝 Generated by `/solve",
33
- ];
34
- /**
35
- * Pattern to extract phases from solve workflow
36
- * Matches: `/spec`, `/exec`, `/test`, `/qa`, etc.
37
- * Also matches without slash: `spec`, `exec`, `test`, `qa` (for arrow notation)
38
- */
39
- const PHASE_PATTERN = /\/?(?<!\w)(spec|exec|test|qa|security-review|testgen|loop)(?!\w)/g;
40
- /**
41
- * Pattern to detect quality loop recommendation
42
- */
43
- const QUALITY_LOOP_PATTERNS = [
44
- /quality\s*loop.*auto-enable/i,
45
- /--quality-loop/i,
46
- /quality\s*loop.*recommended/i,
47
- /enable.*quality\s*loop/i,
48
- ];
49
- /**
50
- * Pattern to extract structured data from HTML comment markers
51
- * Matches: <!-- solve:phases=exec,qa -->
52
- */
53
- const SOLVE_HTML_MARKER_PATTERN = /<!--\s*solve:(\w[\w-]*)=([\w,.-]+)\s*-->/g;
54
- /**
55
- * Pattern to extract issue numbers from solve header
56
- * Matches: "## Solve Workflow for Issues: 123, 456"
57
- */
58
- const ISSUE_NUMBER_PATTERN = /#?(\d+)/g;
59
- /**
60
- * Check if a comment is a solve command output
61
- *
62
- * @param body - The comment body
63
- * @returns True if this appears to be a solve comment
64
- */
65
- export function isSolveComment(body) {
66
- return SOLVE_MARKERS.some((marker) => body.includes(marker));
67
- }
68
- /**
69
- * Find the most recent solve comment from a list of comments
70
- *
71
- * @param comments - Array of issue comments
72
- * @returns The solve comment if found, null otherwise
73
- */
74
- export function findSolveComment(comments) {
75
- // Search from most recent to oldest (assuming comments are in chronological order)
76
- for (let i = comments.length - 1; i >= 0; i--) {
77
- if (isSolveComment(comments[i].body)) {
78
- return comments[i];
79
- }
80
- }
81
- return null;
82
- }
83
- /**
84
- * Parse phases from a solve workflow string
85
- *
86
- * @param workflowString - String like "spec → exec → test → qa"
87
- * @returns Array of phases
88
- */
89
- function parseWorkflowString(workflowString) {
90
- const phases = [];
91
- const matches = workflowString.matchAll(PHASE_PATTERN);
92
- for (const match of matches) {
93
- const phase = match[1];
94
- if (!phases.includes(phase)) {
95
- phases.push(phase);
96
- }
97
- }
98
- return phases;
99
- }
100
- /**
101
- * Parse HTML comment markers from a solve comment body
102
- *
103
- * Extracts structured data from markers like:
104
- * - `<!-- solve:phases=exec,qa -->`
105
- * - `<!-- solve:skip-spec=true -->`
106
- * - `<!-- solve:browser-test=false -->`
107
- * - `<!-- solve:quality-loop=true -->`
108
- *
109
- * @param body - The comment body
110
- * @returns Parsed markers
111
- */
112
- export function parseSolveMarkers(body) {
113
- const markers = {};
114
- const matches = body.matchAll(SOLVE_HTML_MARKER_PATTERN);
115
- for (const match of matches) {
116
- const key = match[1];
117
- const value = match[2];
118
- switch (key) {
119
- case "phases":
120
- markers.phases = value.split(",").filter(Boolean);
121
- break;
122
- case "skip-spec":
123
- markers.skipSpec = value === "true";
124
- break;
125
- case "browser-test":
126
- markers.browserTest = value === "true";
127
- break;
128
- case "quality-loop":
129
- markers.qualityLoop = value === "true";
130
- break;
131
- }
132
- }
133
- return markers;
134
- }
135
- /**
136
- * Parse a solve comment to extract workflow information
137
- *
138
- * @param body - The solve comment body
139
- * @returns Parsed workflow result
140
- */
141
- export function parseSolveWorkflow(body) {
142
- const result = {
143
- phases: [],
144
- qualityLoop: false,
145
- issueNumbers: [],
146
- };
147
- // Extract issue numbers from header (both old and new formats)
148
- const headerMatch = body.match(/## Solve (?:Workflow for Issues?|Analysis)(?::\s*|\s+for\s+)([^\n]+)/i);
149
- if (headerMatch) {
150
- const numberMatches = headerMatch[1].matchAll(ISSUE_NUMBER_PATTERN);
151
- for (const match of numberMatches) {
152
- const num = parseInt(match[1], 10);
153
- if (!isNaN(num) && !result.issueNumbers.includes(num)) {
154
- result.issueNumbers.push(num);
155
- }
156
- }
157
- }
158
- // Try HTML comment markers first (most reliable, machine-readable)
159
- const markers = parseSolveMarkers(body);
160
- if (markers.phases && markers.phases.length > 0) {
161
- result.phases = markers.phases;
162
- result.workflowString = markers.phases.join(" → ");
163
- }
164
- if (markers.qualityLoop !== undefined) {
165
- result.qualityLoop = markers.qualityLoop;
166
- }
167
- // If we got phases from markers, we're done
168
- if (result.phases.length > 0) {
169
- return result;
170
- }
171
- // Find workflow lines (e.g., "/spec 152" or "spec → exec → qa")
172
- const lines = body.split("\n");
173
- // First pass: look for arrow notation (most reliable)
174
- for (const line of lines) {
175
- if (line.includes("→") || line.includes("->")) {
176
- const phases = parseWorkflowString(line);
177
- if (phases.length > 0) {
178
- result.phases = phases;
179
- result.workflowString = line.trim();
180
- break;
181
- }
182
- }
183
- }
184
- // Second pass: if no arrow notation found, collect phases from multiple lines
185
- if (result.phases.length === 0) {
186
- const collectedPhases = [];
187
- for (const line of lines) {
188
- // Look for slash command patterns on individual lines
189
- if (line.includes("/spec") ||
190
- line.includes("/exec") ||
191
- line.includes("/test") ||
192
- line.includes("/qa")) {
193
- const linePhases = parseWorkflowString(line);
194
- for (const phase of linePhases) {
195
- if (!collectedPhases.includes(phase)) {
196
- collectedPhases.push(phase);
197
- }
198
- }
199
- }
200
- }
201
- if (collectedPhases.length > 0) {
202
- result.phases = collectedPhases;
203
- result.workflowString = collectedPhases.map((p) => `/${p}`).join(" → ");
204
- }
205
- }
206
- // If no phases found from lines, try full body
207
- if (result.phases.length === 0) {
208
- result.phases = parseWorkflowString(body);
209
- }
210
- // Check for quality loop recommendation (if not already set by markers)
211
- if (!result.qualityLoop) {
212
- for (const pattern of QUALITY_LOOP_PATTERNS) {
213
- if (pattern.test(body)) {
214
- result.qualityLoop = true;
215
- break;
216
- }
217
- }
218
- }
219
- return result;
220
- }
221
- /**
222
- * Convert solve workflow result to phase signals
223
- *
224
- * @param workflow - The parsed solve workflow
225
- * @returns Array of phase signals with 'solve' source
226
- */
227
- export function solveWorkflowToSignals(workflow) {
228
- const signals = [];
229
- for (const phase of workflow.phases) {
230
- signals.push({
231
- phase,
232
- source: "solve",
233
- confidence: "high",
234
- reason: `Recommended by /solve command: ${workflow.workflowString || phase}`,
235
- });
236
- }
237
- if (workflow.qualityLoop) {
238
- signals.push({
239
- phase: "quality-loop",
240
- source: "solve",
241
- confidence: "high",
242
- reason: "Quality loop recommended by /solve command",
243
- });
244
- }
245
- return signals;
246
- }
247
- /**
248
- * Check if solve comment covers the current issue
249
- *
250
- * @param workflow - The parsed solve workflow
251
- * @param issueNumber - The current issue number
252
- * @returns True if the solve comment includes this issue
253
- */
254
- export function solveCoversIssue(workflow, issueNumber) {
255
- return workflow.issueNumbers.includes(issueNumber);
256
- }
16
+ // Re-export everything from the unified assess-comment-parser
17
+ export {
18
+ // New names (preferred)
19
+ isAssessComment, findAssessComment, parseAssessMarkers, parseAssessWorkflow, assessWorkflowToSignals, assessCoversIssue,
20
+ // Legacy names (deprecated but still functional)
21
+ isSolveComment, findSolveComment, parseSolveMarkers, parseSolveWorkflow, solveWorkflowToSignals, solveCoversIssue, } from "./assess-comment-parser.js";
@@ -37,6 +37,9 @@ export interface PackageManagerConfig {
37
37
  exec: string;
38
38
  install: string;
39
39
  installSilent: string;
40
+ addPkg: string;
41
+ removePkg: string;
42
+ updatePkg: string;
40
43
  }
41
44
  /**
42
45
  * Package manager configurations
@@ -50,6 +53,11 @@ export declare const PM_CONFIG: Record<PackageManager, PackageManagerConfig>;
50
53
  * Falls back to pip if no lockfile found but pyproject.toml/requirements.txt exists
51
54
  */
52
55
  export declare function detectPackageManager(): Promise<PackageManager | null>;
56
+ /**
57
+ * Synchronous version of detectPackageManager for use in startup code.
58
+ * Only checks JS lockfiles (not Python) since sequant is a Node.js tool.
59
+ */
60
+ export declare function detectPackageManagerSync(): PackageManager;
53
61
  /**
54
62
  * Get package manager command configuration
55
63
  */
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Stack detection and configuration
3
3
  */
4
+ import { existsSync } from "fs";
4
5
  import { readdir } from "fs/promises";
5
6
  import { fileExists, readFile } from "./fs.js";
6
7
  /**
@@ -29,24 +30,36 @@ export const PM_CONFIG = {
29
30
  exec: "npx",
30
31
  install: "npm install",
31
32
  installSilent: "npm install --silent",
33
+ addPkg: "npm install",
34
+ removePkg: "npm uninstall",
35
+ updatePkg: "npm update",
32
36
  },
33
37
  bun: {
34
38
  run: "bun run",
35
39
  exec: "bunx",
36
40
  install: "bun install",
37
41
  installSilent: "bun install --silent",
42
+ addPkg: "bun add",
43
+ removePkg: "bun remove",
44
+ updatePkg: "bun update",
38
45
  },
39
46
  yarn: {
40
47
  run: "yarn",
41
48
  exec: "yarn dlx",
42
49
  install: "yarn install",
43
50
  installSilent: "yarn install --silent",
51
+ addPkg: "yarn add",
52
+ removePkg: "yarn remove",
53
+ updatePkg: "yarn upgrade",
44
54
  },
45
55
  pnpm: {
46
56
  run: "pnpm run",
47
57
  exec: "pnpm dlx",
48
58
  install: "pnpm install",
49
59
  installSilent: "pnpm install --silent",
60
+ addPkg: "pnpm add",
61
+ removePkg: "pnpm remove",
62
+ updatePkg: "pnpm update",
50
63
  },
51
64
  // Python package managers
52
65
  pip: {
@@ -54,18 +67,27 @@ export const PM_CONFIG = {
54
67
  exec: "python -m",
55
68
  install: "pip install",
56
69
  installSilent: "pip install -q",
70
+ addPkg: "pip install",
71
+ removePkg: "pip uninstall",
72
+ updatePkg: "pip install --upgrade",
57
73
  },
58
74
  poetry: {
59
75
  run: "poetry run",
60
76
  exec: "poetry run",
61
77
  install: "poetry install",
62
78
  installSilent: "poetry install -q",
79
+ addPkg: "poetry add",
80
+ removePkg: "poetry remove",
81
+ updatePkg: "poetry update",
63
82
  },
64
83
  uv: {
65
84
  run: "uv run",
66
85
  exec: "uvx",
67
86
  install: "uv pip install",
68
87
  installSilent: "uv pip install -q",
88
+ addPkg: "uv pip install",
89
+ removePkg: "uv pip uninstall",
90
+ updatePkg: "uv pip install --upgrade",
69
91
  },
70
92
  };
71
93
  /**
@@ -118,6 +140,18 @@ export async function detectPackageManager() {
118
140
  // Not a recognized project type
119
141
  return null;
120
142
  }
143
+ /**
144
+ * Synchronous version of detectPackageManager for use in startup code.
145
+ * Only checks JS lockfiles (not Python) since sequant is a Node.js tool.
146
+ */
147
+ export function detectPackageManagerSync() {
148
+ for (const { file, pm } of LOCKFILE_PRIORITY) {
149
+ if (existsSync(file)) {
150
+ return pm;
151
+ }
152
+ }
153
+ return "npm";
154
+ }
121
155
  /**
122
156
  * Get package manager command configuration
123
157
  */
@@ -3,6 +3,7 @@
3
3
  */
4
4
  import { execSync } from "child_process";
5
5
  import fs from "fs";
6
+ import { GitHubProvider } from "./workflow/platforms/github.js";
6
7
  /**
7
8
  * Check if a command exists on the system
8
9
  */
@@ -23,13 +24,8 @@ export function commandExists(cmd) {
23
24
  * Check if gh CLI is authenticated
24
25
  */
25
26
  export function isGhAuthenticated() {
26
- try {
27
- execSync("gh auth status", { stdio: "ignore" });
28
- return true;
29
- }
30
- catch {
31
- return false;
32
- }
27
+ const github = new GitHubProvider();
28
+ return github.checkAuthSync();
33
29
  }
34
30
  /**
35
31
  * Check if running in Windows Subsystem for Linux (WSL)
@@ -56,6 +56,8 @@ export interface TautologyFileResult {
56
56
  parseSuccess: boolean;
57
57
  /** Error message if parsing failed */
58
58
  parseError?: string;
59
+ /** Whether the file was skipped via @tautology-skip pragma */
60
+ skipped?: boolean;
59
61
  }
60
62
  /**
61
63
  * Overall tautology detection results
@@ -101,6 +103,14 @@ export declare function extractTestBlocks(content: string): Array<{
101
103
  * Check if a test block contains calls to any of the imported production functions
102
104
  */
103
105
  export declare function testBlockCallsProductionCode(body: string, importedFunctions: ImportedFunction[]): boolean;
106
+ /**
107
+ * Check if a file opts out of tautology detection via pragma comment.
108
+ *
109
+ * Recognized pragmas (must appear in the first 10 lines):
110
+ * // @tautology-skip: <reason>
111
+ * // @tautology-skip
112
+ */
113
+ export declare function hasTautologySkipPragma(content: string): boolean;
104
114
  /**
105
115
  * Analyze a single test file for tautological tests
106
116
  */
@@ -360,10 +360,33 @@ export function testBlockCallsProductionCode(body, importedFunctions) {
360
360
  }
361
361
  return false;
362
362
  }
363
+ /**
364
+ * Check if a file opts out of tautology detection via pragma comment.
365
+ *
366
+ * Recognized pragmas (must appear in the first 10 lines):
367
+ * // @tautology-skip: <reason>
368
+ * // @tautology-skip
369
+ */
370
+ export function hasTautologySkipPragma(content) {
371
+ const headerLines = content.split("\n").slice(0, 10);
372
+ return headerLines.some((line) => /\/\/\s*@tautology-skip/.test(line));
373
+ }
363
374
  /**
364
375
  * Analyze a single test file for tautological tests
365
376
  */
366
377
  export function analyzeTestFile(content, filePath) {
378
+ if (hasTautologySkipPragma(content)) {
379
+ return {
380
+ filePath,
381
+ totalTests: 0,
382
+ tautologicalCount: 0,
383
+ tautologicalPercentage: 0,
384
+ testBlocks: [],
385
+ importedFunctions: [],
386
+ parseSuccess: true,
387
+ skipped: true,
388
+ };
389
+ }
367
390
  try {
368
391
  const importedFunctions = extractImports(content);
369
392
  const testBlocks = extractTestBlocks(content);
@@ -404,9 +427,11 @@ export function analyzeTestFile(content, filePath) {
404
427
  */
405
428
  export function detectTautologicalTests(files) {
406
429
  const fileResults = files.map((file) => analyzeTestFile(file.content, file.path));
407
- const totalFiles = fileResults.length;
408
- const totalTests = fileResults.reduce((sum, r) => sum + r.totalTests, 0);
409
- const totalTautological = fileResults.reduce((sum, r) => sum + r.tautologicalCount, 0);
430
+ // Exclude skipped files from summary counts
431
+ const analyzed = fileResults.filter((r) => !r.skipped);
432
+ const totalFiles = analyzed.length;
433
+ const totalTests = analyzed.reduce((sum, r) => sum + r.totalTests, 0);
434
+ const totalTautological = analyzed.reduce((sum, r) => sum + r.tautologicalCount, 0);
410
435
  const overallPercentage = totalTests > 0 ? (totalTautological / totalTests) * 100 : 0;
411
436
  return {
412
437
  fileResults,
@@ -429,7 +454,21 @@ export function formatTautologyResults(results) {
429
454
  // Summary table
430
455
  lines.push("| Category | Status | Notes |");
431
456
  lines.push("|----------|--------|-------|");
432
- if (results.summary.totalTests === 0) {
457
+ // Skipped files (via @tautology-skip pragma)
458
+ const skippedFiles = results.fileResults.filter((r) => r.skipped);
459
+ if (skippedFiles.length > 0) {
460
+ lines.push(`| Tautology Check | ⏭️ SKIP | ${skippedFiles.length} file(s) skipped via @tautology-skip |`);
461
+ lines.push("");
462
+ lines.push("**Skipped (@tautology-skip):**");
463
+ for (const file of skippedFiles) {
464
+ lines.push(`- \`${file.filePath}\``);
465
+ }
466
+ lines.push("");
467
+ if (results.summary.totalTests === 0) {
468
+ return lines.join("\n");
469
+ }
470
+ }
471
+ else if (results.summary.totalTests === 0) {
433
472
  lines.push("| Tautology Check | ⏭️ SKIP | No test blocks found |");
434
473
  return lines.join("\n");
435
474
  }