sequant 1.20.3 → 2.0.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 (137) hide show
  1. package/.claude-plugin/marketplace.json +2 -4
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.md +29 -9
  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 +18 -86
  63. package/dist/src/lib/workflow/batch-executor.js +232 -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 +340 -220
  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 +208 -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 +10 -1
  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
@@ -0,0 +1,137 @@
1
+ /**
2
+ * Assess Comment Parser
3
+ *
4
+ * Detects and parses /assess command output from GitHub issue comments.
5
+ * This is the unified parser that handles both the new /assess format
6
+ * and the legacy /solve format for backward compatibility.
7
+ *
8
+ * When an assess comment exists, its phase recommendations take precedence
9
+ * over content analysis (but not over labels).
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { findAssessComment, parseAssessWorkflow } from './assess-comment-parser';
14
+ *
15
+ * const comments = [
16
+ * { body: "Some regular comment" },
17
+ * { body: "## Assess Analysis\n\n→ PROCEED — Ready.\n\n<!-- assess:phases=spec,exec,qa -->" },
18
+ * ];
19
+ *
20
+ * const assessComment = findAssessComment(comments);
21
+ * if (assessComment) {
22
+ * const workflow = parseAssessWorkflow(assessComment.body);
23
+ * // workflow: { phases: ['spec', 'exec', 'qa'], action: 'PROCEED', ... }
24
+ * }
25
+ * ```
26
+ */
27
+ import type { Phase } from "./workflow/types.js";
28
+ import type { PhaseSignal } from "./phase-signal.js";
29
+ /**
30
+ * The 6 fixed actions that /assess can recommend
31
+ */
32
+ export type AssessAction = "PROCEED" | "CLOSE" | "MERGE" | "REWRITE" | "CLARIFY" | "PARK";
33
+ /**
34
+ * Result of parsing an assess comment (superset of SolveWorkflowResult)
35
+ */
36
+ export interface AssessWorkflowResult {
37
+ /** Phases recommended by assess (only meaningful for PROCEED) */
38
+ phases: Phase[];
39
+ /** Whether quality loop is recommended */
40
+ qualityLoop: boolean;
41
+ /** The issue numbers mentioned in the assess comment */
42
+ issueNumbers: number[];
43
+ /** Raw workflow string (e.g., "spec → exec → qa") */
44
+ workflowString?: string;
45
+ /** The recommended action (one of the 6 fixed actions) */
46
+ action?: AssessAction;
47
+ }
48
+ /**
49
+ * Backward-compatible alias for AssessWorkflowResult
50
+ */
51
+ export type SolveWorkflowResult = AssessWorkflowResult;
52
+ /**
53
+ * Comment structure (simplified from GitHub API)
54
+ */
55
+ export interface IssueComment {
56
+ body: string;
57
+ author?: {
58
+ login: string;
59
+ };
60
+ createdAt?: string;
61
+ }
62
+ /**
63
+ * Structured data extracted from HTML comment markers
64
+ */
65
+ export interface AssessMarkers {
66
+ /** Recommended phases */
67
+ phases?: string[];
68
+ /** Whether quality loop is recommended */
69
+ qualityLoop?: boolean;
70
+ /** The recommended action */
71
+ action?: AssessAction;
72
+ }
73
+ /**
74
+ * Backward-compatible alias for AssessMarkers
75
+ */
76
+ export type SolveMarkers = AssessMarkers;
77
+ /**
78
+ * Check if a comment is an assess command output (new format)
79
+ */
80
+ export declare function isAssessComment(body: string): boolean;
81
+ /**
82
+ * Check if a comment is a solve command output (legacy format)
83
+ * @deprecated Use isAssessComment instead
84
+ */
85
+ export declare function isSolveComment(body: string): boolean;
86
+ /**
87
+ * Find the most recent assess/solve comment from a list of comments
88
+ */
89
+ export declare function findAssessComment(comments: IssueComment[]): IssueComment | null;
90
+ /**
91
+ * Find the most recent solve comment from a list of comments
92
+ * @deprecated Use findAssessComment instead
93
+ */
94
+ export declare function findSolveComment(comments: IssueComment[]): IssueComment | null;
95
+ /**
96
+ * Parse HTML comment markers from a comment body
97
+ *
98
+ * Supports both `assess:` and `solve:` prefixes.
99
+ * When both are present, `assess:` markers take precedence.
100
+ */
101
+ export declare function parseAssessMarkers(body: string): AssessMarkers;
102
+ /**
103
+ * Parse HTML comment markers (backward-compatible alias)
104
+ * @deprecated Use parseAssessMarkers instead
105
+ */
106
+ export declare function parseSolveMarkers(body: string): AssessMarkers;
107
+ /**
108
+ * Parse an assess/solve comment to extract workflow information
109
+ */
110
+ export declare function parseAssessWorkflow(body: string): AssessWorkflowResult;
111
+ /**
112
+ * Parse a solve comment to extract workflow information
113
+ * @deprecated Use parseAssessWorkflow instead
114
+ */
115
+ export declare function parseSolveWorkflow(body: string): AssessWorkflowResult;
116
+ /**
117
+ * Convert assess workflow result to phase signals
118
+ */
119
+ export declare function assessWorkflowToSignals(workflow: AssessWorkflowResult): PhaseSignal[];
120
+ /**
121
+ * Convert solve workflow result to phase signals
122
+ *
123
+ * Preserves "/solve" wording in reason strings for backward compatibility
124
+ * with existing tests and consumers.
125
+ *
126
+ * @deprecated Use assessWorkflowToSignals instead
127
+ */
128
+ export declare function solveWorkflowToSignals(workflow: AssessWorkflowResult): PhaseSignal[];
129
+ /**
130
+ * Check if assess/solve comment covers the current issue
131
+ */
132
+ export declare function assessCoversIssue(workflow: AssessWorkflowResult, issueNumber: number): boolean;
133
+ /**
134
+ * Check if solve comment covers the current issue
135
+ * @deprecated Use assessCoversIssue instead
136
+ */
137
+ export declare function solveCoversIssue(workflow: AssessWorkflowResult, issueNumber: number): boolean;
@@ -0,0 +1,344 @@
1
+ /**
2
+ * Assess Comment Parser
3
+ *
4
+ * Detects and parses /assess command output from GitHub issue comments.
5
+ * This is the unified parser that handles both the new /assess format
6
+ * and the legacy /solve format for backward compatibility.
7
+ *
8
+ * When an assess comment exists, its phase recommendations take precedence
9
+ * over content analysis (but not over labels).
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { findAssessComment, parseAssessWorkflow } from './assess-comment-parser';
14
+ *
15
+ * const comments = [
16
+ * { body: "Some regular comment" },
17
+ * { body: "## Assess Analysis\n\n→ PROCEED — Ready.\n\n<!-- assess:phases=spec,exec,qa -->" },
18
+ * ];
19
+ *
20
+ * const assessComment = findAssessComment(comments);
21
+ * if (assessComment) {
22
+ * const workflow = parseAssessWorkflow(assessComment.body);
23
+ * // workflow: { phases: ['spec', 'exec', 'qa'], action: 'PROCEED', ... }
24
+ * }
25
+ * ```
26
+ */
27
+ /**
28
+ * Markers that indicate an assess comment (new format)
29
+ */
30
+ const ASSESS_MARKERS = [
31
+ "## Assess Analysis",
32
+ "## Assess Workflow",
33
+ "*Generated by `/assess",
34
+ ];
35
+ /**
36
+ * Markers that indicate a solve comment (legacy format)
37
+ */
38
+ const SOLVE_MARKERS = [
39
+ "## Solve Analysis",
40
+ "## Solve Workflow for Issues:",
41
+ "## Solve Workflow for Issue:",
42
+ "### Recommended Workflow",
43
+ "*📝 Generated by `/solve",
44
+ ];
45
+ /**
46
+ * All markers (both assess and solve)
47
+ */
48
+ const ALL_MARKERS = [...ASSESS_MARKERS, ...SOLVE_MARKERS];
49
+ /**
50
+ * Pattern to extract phases from workflow string
51
+ * Matches: `/spec`, `/exec`, `/test`, `/qa`, etc.
52
+ * Also matches without slash: `spec`, `exec`, `test`, `qa` (for arrow notation)
53
+ */
54
+ const PHASE_PATTERN = /\/?(?<!\w)(spec|exec|test|qa|security-review|testgen|loop)(?!\w)/g;
55
+ /**
56
+ * Pattern to detect quality loop recommendation
57
+ */
58
+ const QUALITY_LOOP_PATTERNS = [
59
+ /quality\s*loop.*auto-enable/i,
60
+ /--quality-loop/i,
61
+ /quality\s*loop.*recommended/i,
62
+ /enable.*quality\s*loop/i,
63
+ ];
64
+ /**
65
+ * Pattern to extract structured data from HTML comment markers
66
+ * Matches both: <!-- assess:phases=exec,qa --> and <!-- solve:phases=exec,qa -->
67
+ */
68
+ const HTML_MARKER_PATTERN = /<!--\s*(?:assess|solve):(\w[\w-]*)=([\w,.-]*)\s*-->/g;
69
+ /**
70
+ * Pattern to extract assess-specific HTML markers (takes precedence)
71
+ */
72
+ const ASSESS_HTML_MARKER_PATTERN = /<!--\s*assess:(\w[\w-]*)=([\w,.-]*)\s*-->/g;
73
+ /**
74
+ * Pattern to extract issue numbers from header
75
+ * Matches: "## Solve Workflow for Issues: 123, 456" or "#123"
76
+ */
77
+ const ISSUE_NUMBER_PATTERN = /#?(\d+)/g;
78
+ /**
79
+ * Valid assess actions
80
+ */
81
+ const VALID_ACTIONS = [
82
+ "PROCEED",
83
+ "CLOSE",
84
+ "MERGE",
85
+ "REWRITE",
86
+ "CLARIFY",
87
+ "PARK",
88
+ ];
89
+ // ─── Detection Functions ────────────────────────────────────────────────────
90
+ /**
91
+ * Check if a comment is an assess command output (new format)
92
+ */
93
+ export function isAssessComment(body) {
94
+ return ALL_MARKERS.some((marker) => body.includes(marker));
95
+ }
96
+ /**
97
+ * Check if a comment is a solve command output (legacy format)
98
+ * @deprecated Use isAssessComment instead
99
+ */
100
+ export function isSolveComment(body) {
101
+ return isAssessComment(body);
102
+ }
103
+ /**
104
+ * Find the most recent assess/solve comment from a list of comments
105
+ */
106
+ export function findAssessComment(comments) {
107
+ for (let i = comments.length - 1; i >= 0; i--) {
108
+ if (isAssessComment(comments[i].body)) {
109
+ return comments[i];
110
+ }
111
+ }
112
+ return null;
113
+ }
114
+ /**
115
+ * Find the most recent solve comment from a list of comments
116
+ * @deprecated Use findAssessComment instead
117
+ */
118
+ export function findSolveComment(comments) {
119
+ return findAssessComment(comments);
120
+ }
121
+ // ─── Parsing Functions ──────────────────────────────────────────────────────
122
+ /**
123
+ * Parse phases from a workflow string
124
+ */
125
+ function parseWorkflowString(workflowString) {
126
+ const phases = [];
127
+ const matches = workflowString.matchAll(PHASE_PATTERN);
128
+ for (const match of matches) {
129
+ const phase = match[1];
130
+ if (!phases.includes(phase)) {
131
+ phases.push(phase);
132
+ }
133
+ }
134
+ return phases;
135
+ }
136
+ /**
137
+ * Parse HTML comment markers from a comment body
138
+ *
139
+ * Supports both `assess:` and `solve:` prefixes.
140
+ * When both are present, `assess:` markers take precedence.
141
+ */
142
+ export function parseAssessMarkers(body) {
143
+ const markers = {};
144
+ // First pass: collect all markers (both assess: and solve:)
145
+ const allMatches = body.matchAll(HTML_MARKER_PATTERN);
146
+ for (const match of allMatches) {
147
+ applyMarker(markers, match[1], match[2]);
148
+ }
149
+ // Second pass: assess: markers override solve: markers
150
+ const assessMatches = body.matchAll(ASSESS_HTML_MARKER_PATTERN);
151
+ for (const match of assessMatches) {
152
+ applyMarker(markers, match[1], match[2]);
153
+ }
154
+ return markers;
155
+ }
156
+ /**
157
+ * Apply a single marker key-value pair to the markers object
158
+ */
159
+ function applyMarker(markers, key, value) {
160
+ switch (key) {
161
+ case "phases":
162
+ markers.phases = value.split(",").filter(Boolean);
163
+ break;
164
+ case "quality-loop":
165
+ markers.qualityLoop = value === "true";
166
+ break;
167
+ case "action":
168
+ if (VALID_ACTIONS.includes(value)) {
169
+ markers.action = value;
170
+ }
171
+ break;
172
+ }
173
+ }
174
+ /**
175
+ * Parse HTML comment markers (backward-compatible alias)
176
+ * @deprecated Use parseAssessMarkers instead
177
+ */
178
+ export function parseSolveMarkers(body) {
179
+ return parseAssessMarkers(body);
180
+ }
181
+ /**
182
+ * Parse an assess/solve comment to extract workflow information
183
+ */
184
+ export function parseAssessWorkflow(body) {
185
+ const result = {
186
+ phases: [],
187
+ qualityLoop: false,
188
+ issueNumbers: [],
189
+ };
190
+ // Extract action from "→ ACTION — reason" line
191
+ const actionMatch = body.match(/→\s*(PROCEED|CLOSE|MERGE|REWRITE|CLARIFY|PARK)\s*[—-]/);
192
+ if (actionMatch) {
193
+ result.action = actionMatch[1];
194
+ }
195
+ // Extract issue numbers from header (both old and new formats)
196
+ const headerMatch = body.match(/## (?:Assess|Solve) (?:Workflow for Issues?|Analysis)(?::\s*|\s+for\s+)([^\n]+)/i);
197
+ if (headerMatch) {
198
+ const numberMatches = headerMatch[1].matchAll(ISSUE_NUMBER_PATTERN);
199
+ for (const match of numberMatches) {
200
+ const num = parseInt(match[1], 10);
201
+ if (!isNaN(num) && !result.issueNumbers.includes(num)) {
202
+ result.issueNumbers.push(num);
203
+ }
204
+ }
205
+ }
206
+ // Try HTML comment markers first (most reliable, machine-readable)
207
+ const markers = parseAssessMarkers(body);
208
+ // Action from markers overrides inline detection
209
+ if (markers.action) {
210
+ result.action = markers.action;
211
+ }
212
+ if (markers.phases && markers.phases.length > 0) {
213
+ result.phases = markers.phases;
214
+ result.workflowString = markers.phases.join(" → ");
215
+ }
216
+ if (markers.qualityLoop !== undefined) {
217
+ result.qualityLoop = markers.qualityLoop;
218
+ }
219
+ // If we got phases from markers, we're done with phase detection
220
+ if (result.phases.length > 0) {
221
+ return result;
222
+ }
223
+ // Find workflow lines (e.g., "/spec 152" or "spec → exec → qa")
224
+ const lines = body.split("\n");
225
+ // First pass: look for arrow notation (most reliable)
226
+ for (const line of lines) {
227
+ if (line.includes("→") || line.includes("->")) {
228
+ const phases = parseWorkflowString(line);
229
+ if (phases.length > 0) {
230
+ result.phases = phases;
231
+ result.workflowString = line.trim();
232
+ break;
233
+ }
234
+ }
235
+ }
236
+ // Second pass: if no arrow notation found, collect phases from multiple lines
237
+ if (result.phases.length === 0) {
238
+ const collectedPhases = [];
239
+ for (const line of lines) {
240
+ if (line.includes("/spec") ||
241
+ line.includes("/exec") ||
242
+ line.includes("/test") ||
243
+ line.includes("/qa")) {
244
+ const linePhases = parseWorkflowString(line);
245
+ for (const phase of linePhases) {
246
+ if (!collectedPhases.includes(phase)) {
247
+ collectedPhases.push(phase);
248
+ }
249
+ }
250
+ }
251
+ }
252
+ if (collectedPhases.length > 0) {
253
+ result.phases = collectedPhases;
254
+ result.workflowString = collectedPhases.map((p) => `/${p}`).join(" → ");
255
+ }
256
+ }
257
+ // If no phases found from lines, try full body
258
+ if (result.phases.length === 0) {
259
+ result.phases = parseWorkflowString(body);
260
+ }
261
+ // Check for quality loop recommendation (if not already set by markers)
262
+ if (!result.qualityLoop) {
263
+ for (const pattern of QUALITY_LOOP_PATTERNS) {
264
+ if (pattern.test(body)) {
265
+ result.qualityLoop = true;
266
+ break;
267
+ }
268
+ }
269
+ }
270
+ return result;
271
+ }
272
+ /**
273
+ * Parse a solve comment to extract workflow information
274
+ * @deprecated Use parseAssessWorkflow instead
275
+ */
276
+ export function parseSolveWorkflow(body) {
277
+ return parseAssessWorkflow(body);
278
+ }
279
+ // ─── Signal Conversion ──────────────────────────────────────────────────────
280
+ /**
281
+ * Convert assess workflow result to phase signals
282
+ */
283
+ export function assessWorkflowToSignals(workflow) {
284
+ const signals = [];
285
+ for (const phase of workflow.phases) {
286
+ signals.push({
287
+ phase,
288
+ source: "assess",
289
+ confidence: "high",
290
+ reason: `Recommended by /assess command: ${workflow.workflowString || phase}`,
291
+ });
292
+ }
293
+ if (workflow.qualityLoop) {
294
+ signals.push({
295
+ phase: "quality-loop",
296
+ source: "assess",
297
+ confidence: "high",
298
+ reason: "Quality loop recommended by /assess command",
299
+ });
300
+ }
301
+ return signals;
302
+ }
303
+ /**
304
+ * Convert solve workflow result to phase signals
305
+ *
306
+ * Preserves "/solve" wording in reason strings for backward compatibility
307
+ * with existing tests and consumers.
308
+ *
309
+ * @deprecated Use assessWorkflowToSignals instead
310
+ */
311
+ export function solveWorkflowToSignals(workflow) {
312
+ const signals = [];
313
+ for (const phase of workflow.phases) {
314
+ signals.push({
315
+ phase,
316
+ source: "solve",
317
+ confidence: "high",
318
+ reason: `Recommended by /solve command: ${workflow.workflowString || phase}`,
319
+ });
320
+ }
321
+ if (workflow.qualityLoop) {
322
+ signals.push({
323
+ phase: "quality-loop",
324
+ source: "solve",
325
+ confidence: "high",
326
+ reason: "Quality loop recommended by /solve command",
327
+ });
328
+ }
329
+ return signals;
330
+ }
331
+ // ─── Utility Functions ──────────────────────────────────────────────────────
332
+ /**
333
+ * Check if assess/solve comment covers the current issue
334
+ */
335
+ export function assessCoversIssue(workflow, issueNumber) {
336
+ return workflow.issueNumbers.includes(issueNumber);
337
+ }
338
+ /**
339
+ * Check if solve comment covers the current issue
340
+ * @deprecated Use assessCoversIssue instead
341
+ */
342
+ export function solveCoversIssue(workflow, issueNumber) {
343
+ return assessCoversIssue(workflow, issueNumber);
344
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Repository-level CI configuration loader.
3
+ *
4
+ * Reads .github/sequant.yml (or .sequant/ci.json) for repo-level defaults
5
+ * that are merged with workflow inputs. This lets teams set default agents,
6
+ * phases, and cost controls without editing workflow files.
7
+ */
8
+ import { type CIConfig } from "./types.js";
9
+ /**
10
+ * Load CI configuration from the repository.
11
+ *
12
+ * Checks (in order):
13
+ * 1. .github/sequant.yml (YAML — parsed as simple key: value)
14
+ * 2. .sequant/ci.json (JSON)
15
+ *
16
+ * Returns CI_DEFAULTS if no config file is found.
17
+ */
18
+ export declare function loadCIConfig(repoRoot: string): CIConfig;
19
+ /**
20
+ * Merge config with defaults (for display/documentation purposes).
21
+ */
22
+ export declare function resolveConfig(config: CIConfig): Required<CIConfig>;
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Repository-level CI configuration loader.
3
+ *
4
+ * Reads .github/sequant.yml (or .sequant/ci.json) for repo-level defaults
5
+ * that are merged with workflow inputs. This lets teams set default agents,
6
+ * phases, and cost controls without editing workflow files.
7
+ */
8
+ import { readFileSync } from "node:fs";
9
+ import { join } from "node:path";
10
+ import { CI_DEFAULTS } from "./types.js";
11
+ const VALID_PHASES = new Set([
12
+ "spec",
13
+ "security-review",
14
+ "testgen",
15
+ "exec",
16
+ "test",
17
+ "qa",
18
+ "loop",
19
+ ]);
20
+ const VALID_AGENTS = new Set(["claude-code", "aider", "codex"]);
21
+ /**
22
+ * Load CI configuration from the repository.
23
+ *
24
+ * Checks (in order):
25
+ * 1. .github/sequant.yml (YAML — parsed as simple key: value)
26
+ * 2. .sequant/ci.json (JSON)
27
+ *
28
+ * Returns CI_DEFAULTS if no config file is found.
29
+ */
30
+ export function loadCIConfig(repoRoot) {
31
+ // Try .github/sequant.yml first
32
+ const yamlPath = join(repoRoot, ".github", "sequant.yml");
33
+ const yamlConfig = tryLoadYaml(yamlPath);
34
+ if (yamlConfig)
35
+ return yamlConfig;
36
+ // Fall back to .sequant/ci.json
37
+ const jsonPath = join(repoRoot, ".sequant", "ci.json");
38
+ const jsonConfig = tryLoadJson(jsonPath);
39
+ if (jsonConfig)
40
+ return jsonConfig;
41
+ return {};
42
+ }
43
+ /**
44
+ * Parse a simple YAML config file (key: value, no nested structures).
45
+ * Avoids adding a YAML parser dependency for a simple flat config.
46
+ */
47
+ function tryLoadYaml(path) {
48
+ let content;
49
+ try {
50
+ content = readFileSync(path, "utf-8");
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ const parsed = {};
56
+ for (const line of content.split("\n")) {
57
+ const trimmed = line.trim();
58
+ if (!trimmed || trimmed.startsWith("#"))
59
+ continue;
60
+ const colonIdx = trimmed.indexOf(":");
61
+ if (colonIdx === -1)
62
+ continue;
63
+ const key = trimmed.slice(0, colonIdx).trim();
64
+ const value = trimmed.slice(colonIdx + 1).trim();
65
+ parsed[key] = value;
66
+ }
67
+ return normalizeConfig(parsed);
68
+ }
69
+ /**
70
+ * Load and parse a JSON config file.
71
+ */
72
+ function tryLoadJson(path) {
73
+ try {
74
+ const content = readFileSync(path, "utf-8");
75
+ const parsed = JSON.parse(content);
76
+ return normalizeConfig(parsed);
77
+ }
78
+ catch {
79
+ return null;
80
+ }
81
+ }
82
+ /**
83
+ * Normalize a raw config object into validated CIConfig.
84
+ */
85
+ function normalizeConfig(raw) {
86
+ const config = {};
87
+ if (typeof raw.agent === "string" && VALID_AGENTS.has(raw.agent)) {
88
+ config.agent = raw.agent;
89
+ }
90
+ if (typeof raw.phases === "string") {
91
+ const phases = raw.phases
92
+ .split(",")
93
+ .map((p) => p.trim())
94
+ .filter((p) => VALID_PHASES.has(p));
95
+ if (phases.length > 0)
96
+ config.phases = phases;
97
+ }
98
+ else if (Array.isArray(raw.phases)) {
99
+ const phases = raw.phases
100
+ .filter((p) => typeof p === "string")
101
+ .filter((p) => VALID_PHASES.has(p));
102
+ if (phases.length > 0)
103
+ config.phases = phases;
104
+ }
105
+ if (typeof raw.timeout === "number" && raw.timeout >= 60) {
106
+ config.timeout = raw.timeout;
107
+ }
108
+ else if (typeof raw.timeout === "string") {
109
+ const n = parseInt(raw.timeout, 10);
110
+ if (!isNaN(n) && n >= 60)
111
+ config.timeout = n;
112
+ }
113
+ if (typeof raw.qualityLoop === "boolean") {
114
+ config.qualityLoop = raw.qualityLoop;
115
+ }
116
+ else if (typeof raw.qualityLoop === "string") {
117
+ config.qualityLoop = raw.qualityLoop === "true";
118
+ }
119
+ const maxRuns = typeof raw.maxConcurrentRuns === "number"
120
+ ? raw.maxConcurrentRuns
121
+ : typeof raw.maxConcurrentRuns === "string"
122
+ ? parseInt(raw.maxConcurrentRuns, 10)
123
+ : NaN;
124
+ if (!isNaN(maxRuns) && maxRuns >= 1) {
125
+ config.maxConcurrentRuns = maxRuns;
126
+ }
127
+ return config;
128
+ }
129
+ /**
130
+ * Merge config with defaults (for display/documentation purposes).
131
+ */
132
+ export function resolveConfig(config) {
133
+ return { ...CI_DEFAULTS, ...config };
134
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * CI integration module — provides everything needed to run sequant
3
+ * workflows from GitHub Actions or other CI/CD systems.
4
+ */
5
+ export { loadCIConfig, resolveConfig } from "./config.js";
6
+ export { parseInputs, validateInputs } from "./inputs.js";
7
+ export type { RawActionInputs } from "./inputs.js";
8
+ export { getFailureLabels, getStartLabels, getStartRemoveLabels, getSuccessLabels, labelCommands, } from "./labels.js";
9
+ export { formatMultiOutputs, formatOutputs, formatSummary, outputCommands, } from "./outputs.js";
10
+ export { detectTrigger } from "./triggers.js";
11
+ export { CI_DEFAULTS, COMMENT_TRIGGER_PATTERN, LIFECYCLE_LABELS, TRIGGER_LABELS, } from "./types.js";
12
+ export type { ActionInputs, ActionOutputs, CIConfig, GitHubContext, GitHubEventPayload, TriggerResult, TriggerType, } from "./types.js";
@@ -0,0 +1,10 @@
1
+ /**
2
+ * CI integration module — provides everything needed to run sequant
3
+ * workflows from GitHub Actions or other CI/CD systems.
4
+ */
5
+ export { loadCIConfig, resolveConfig } from "./config.js";
6
+ export { parseInputs, validateInputs } from "./inputs.js";
7
+ export { getFailureLabels, getStartLabels, getStartRemoveLabels, getSuccessLabels, labelCommands, } from "./labels.js";
8
+ export { formatMultiOutputs, formatOutputs, formatSummary, outputCommands, } from "./outputs.js";
9
+ export { detectTrigger } from "./triggers.js";
10
+ export { CI_DEFAULTS, COMMENT_TRIGGER_PATTERN, LIFECYCLE_LABELS, TRIGGER_LABELS, } from "./types.js";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Input parsing and validation for the GitHub Action.
3
+ *
4
+ * Parses raw string inputs from action.yml into validated ActionInputs,
5
+ * applying defaults and merging with repo-level configuration.
6
+ */
7
+ import type { ActionInputs, CIConfig } from "./types.js";
8
+ /**
9
+ * Raw inputs as received from GitHub Actions (all strings).
10
+ */
11
+ export interface RawActionInputs {
12
+ issues?: string;
13
+ phases?: string;
14
+ agent?: string;
15
+ timeout?: string;
16
+ "quality-loop"?: string;
17
+ "api-key"?: string;
18
+ }
19
+ /**
20
+ * Parse and validate action inputs, merging with repo config.
21
+ *
22
+ * Merge precedence: workflow inputs > config file > action defaults
23
+ */
24
+ export declare function parseInputs(raw: RawActionInputs, config?: CIConfig): ActionInputs;
25
+ /**
26
+ * Validate that required inputs are present and well-formed.
27
+ * Returns an array of error messages (empty = valid).
28
+ */
29
+ export declare function validateInputs(inputs: ActionInputs): string[];