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.
- package/.claude-plugin/marketplace.json +2 -4
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +36 -15
- package/dist/bin/cli.js +25 -2
- package/dist/src/commands/doctor.js +42 -9
- package/dist/src/commands/init.d.ts +1 -0
- package/dist/src/commands/init.js +52 -0
- package/dist/src/commands/logs.d.ts +1 -0
- package/dist/src/commands/logs.js +18 -2
- package/dist/src/commands/run.d.ts +7 -0
- package/dist/src/commands/run.js +235 -68
- package/dist/src/commands/serve.d.ts +13 -0
- package/dist/src/commands/serve.js +131 -0
- package/dist/src/commands/stats.d.ts +1 -0
- package/dist/src/commands/stats.js +185 -26
- package/dist/src/commands/status.d.ts +2 -0
- package/dist/src/commands/status.js +99 -50
- package/dist/src/index.d.ts +2 -2
- package/dist/src/index.js +4 -1
- package/dist/src/lib/ac-parser.d.ts +2 -0
- package/dist/src/lib/ac-parser.js +12 -2
- package/dist/src/lib/assess-comment-parser.d.ts +137 -0
- package/dist/src/lib/assess-comment-parser.js +344 -0
- package/dist/src/lib/ci/config.d.ts +22 -0
- package/dist/src/lib/ci/config.js +134 -0
- package/dist/src/lib/ci/index.d.ts +12 -0
- package/dist/src/lib/ci/index.js +10 -0
- package/dist/src/lib/ci/inputs.d.ts +29 -0
- package/dist/src/lib/ci/inputs.js +103 -0
- package/dist/src/lib/ci/labels.d.ts +34 -0
- package/dist/src/lib/ci/labels.js +101 -0
- package/dist/src/lib/ci/outputs.d.ts +25 -0
- package/dist/src/lib/ci/outputs.js +84 -0
- package/dist/src/lib/ci/triggers.d.ts +9 -0
- package/dist/src/lib/ci/triggers.js +86 -0
- package/dist/src/lib/ci/types.d.ts +131 -0
- package/dist/src/lib/ci/types.js +47 -0
- package/dist/src/lib/mcp-config.d.ts +54 -0
- package/dist/src/lib/mcp-config.js +172 -0
- package/dist/src/lib/merge-check/index.js +6 -12
- package/dist/src/lib/merge-check/types.d.ts +20 -7
- package/dist/src/lib/merge-check/types.js +11 -0
- package/dist/src/lib/phase-signal.d.ts +3 -3
- package/dist/src/lib/phase-signal.js +5 -3
- package/dist/src/lib/settings.d.ts +52 -0
- package/dist/src/lib/settings.js +41 -0
- package/dist/src/lib/shutdown.d.ts +16 -5
- package/dist/src/lib/shutdown.js +32 -12
- package/dist/src/lib/solve-comment-parser.d.ts +9 -102
- package/dist/src/lib/solve-comment-parser.js +13 -248
- package/dist/src/lib/stacks.d.ts +8 -0
- package/dist/src/lib/stacks.js +34 -0
- package/dist/src/lib/system.js +3 -7
- package/dist/src/lib/test-tautology-detector.d.ts +10 -0
- package/dist/src/lib/test-tautology-detector.js +43 -4
- package/dist/src/lib/upstream/assessment.js +9 -59
- package/dist/src/lib/upstream/issues.js +12 -75
- package/dist/src/lib/version-check.d.ts +2 -2
- package/dist/src/lib/version-check.js +6 -3
- package/dist/src/lib/version.d.ts +4 -0
- package/dist/src/lib/version.js +25 -0
- package/dist/src/lib/workflow/batch-executor.d.ts +26 -86
- package/dist/src/lib/workflow/batch-executor.js +269 -55
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +56 -0
- package/dist/src/lib/workflow/drivers/agent-driver.js +8 -0
- package/dist/src/lib/workflow/drivers/aider.d.ts +18 -0
- package/dist/src/lib/workflow/drivers/aider.js +160 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +17 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +165 -0
- package/dist/src/lib/workflow/drivers/index.d.ts +20 -0
- package/dist/src/lib/workflow/drivers/index.js +27 -0
- package/dist/src/lib/workflow/error-classifier.d.ts +16 -0
- package/dist/src/lib/workflow/error-classifier.js +90 -0
- package/dist/src/lib/workflow/log-writer.d.ts +6 -3
- package/dist/src/lib/workflow/log-writer.js +57 -27
- package/dist/src/lib/workflow/metrics-schema.d.ts +9 -9
- package/dist/src/lib/workflow/phase-detection.d.ts +23 -0
- package/dist/src/lib/workflow/phase-detection.js +45 -29
- package/dist/src/lib/workflow/phase-executor.d.ts +42 -3
- package/dist/src/lib/workflow/phase-executor.js +375 -229
- package/dist/src/lib/workflow/phase-mapper.d.ts +1 -1
- package/dist/src/lib/workflow/phase-mapper.js +7 -7
- package/dist/src/lib/workflow/platforms/github.d.ts +157 -0
- package/dist/src/lib/workflow/platforms/github.js +466 -0
- package/dist/src/lib/workflow/platforms/index.d.ts +17 -0
- package/dist/src/lib/workflow/platforms/index.js +25 -0
- package/dist/src/lib/workflow/platforms/platform-provider.d.ts +67 -0
- package/dist/src/lib/workflow/platforms/platform-provider.js +8 -0
- package/dist/src/lib/workflow/pr-status.d.ts +2 -4
- package/dist/src/lib/workflow/pr-status.js +3 -16
- package/dist/src/lib/workflow/qa-cache.d.ts +58 -0
- package/dist/src/lib/workflow/qa-cache.js +88 -0
- package/dist/src/lib/workflow/reconcile.d.ts +69 -0
- package/dist/src/lib/workflow/reconcile.js +290 -0
- package/dist/src/lib/workflow/ring-buffer.d.ts +17 -0
- package/dist/src/lib/workflow/ring-buffer.js +37 -0
- package/dist/src/lib/workflow/run-log-schema.d.ts +115 -24
- package/dist/src/lib/workflow/run-log-schema.js +47 -12
- package/dist/src/lib/workflow/run-reflect.js +1 -1
- package/dist/src/lib/workflow/state-cleanup.js +21 -0
- package/dist/src/lib/workflow/state-manager.d.ts +34 -3
- package/dist/src/lib/workflow/state-manager.js +278 -126
- package/dist/src/lib/workflow/state-schema.d.ts +34 -30
- package/dist/src/lib/workflow/state-schema.js +35 -25
- package/dist/src/lib/workflow/state-utils.d.ts +3 -1
- package/dist/src/lib/workflow/state-utils.js +1 -0
- package/dist/src/lib/workflow/types.d.ts +224 -6
- package/dist/src/lib/workflow/types.js +20 -1
- package/dist/src/lib/workflow/worktree-discovery.d.ts +1 -1
- package/dist/src/lib/workflow/worktree-discovery.js +6 -14
- package/dist/src/lib/workflow/worktree-manager.js +33 -51
- package/dist/src/mcp/index.d.ts +4 -0
- package/dist/src/mcp/index.js +4 -0
- package/dist/src/mcp/resources.d.ts +7 -0
- package/dist/src/mcp/resources.js +111 -0
- package/dist/src/mcp/run-registry.d.ts +34 -0
- package/dist/src/mcp/run-registry.js +42 -0
- package/dist/src/mcp/server.d.ts +12 -0
- package/dist/src/mcp/server.js +50 -0
- package/dist/src/mcp/tools/logs.d.ts +7 -0
- package/dist/src/mcp/tools/logs.js +149 -0
- package/dist/src/mcp/tools/run.d.ts +121 -0
- package/dist/src/mcp/tools/run.js +591 -0
- package/dist/src/mcp/tools/status.d.ts +7 -0
- package/dist/src/mcp/tools/status.js +127 -0
- package/package.json +26 -7
- package/templates/hooks/post-tool.sh +19 -8
- package/templates/hooks/pre-tool.sh +36 -49
- package/templates/mcp.json +6 -0
- package/templates/skills/assess/SKILL.md +354 -352
- package/templates/skills/exec/SKILL.md +64 -1
- package/templates/skills/fullsolve/SKILL.md +35 -4
- package/templates/skills/qa/SKILL.md +486 -9
- package/templates/skills/qa/scripts/quality-checks.sh +1 -1
- package/templates/skills/setup/SKILL.md +386 -0
- package/templates/skills/solve/SKILL.md +38 -664
- 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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
7
|
+
* Migration:
|
|
9
8
|
* ```typescript
|
|
9
|
+
* // Before (deprecated):
|
|
10
10
|
* import { findSolveComment, parseSolveWorkflow } from './solve-comment-parser';
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
|
|
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
|
-
*
|
|
5
|
-
*
|
|
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
|
-
*
|
|
7
|
+
* Migration:
|
|
9
8
|
* ```typescript
|
|
9
|
+
* // Before (deprecated):
|
|
10
10
|
* import { findSolveComment, parseSolveWorkflow } from './solve-comment-parser';
|
|
11
11
|
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
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
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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";
|
package/dist/src/lib/stacks.d.ts
CHANGED
|
@@ -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
|
*/
|
package/dist/src/lib/stacks.js
CHANGED
|
@@ -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
|
*/
|
package/dist/src/lib/system.js
CHANGED
|
@@ -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
|
-
|
|
27
|
-
|
|
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
|
-
|
|
408
|
-
const
|
|
409
|
-
const
|
|
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
|
-
|
|
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
|
}
|