sequant 1.20.2 → 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.
- package/.claude-plugin/marketplace.json +2 -4
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +29 -9
- 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 +18 -86
- package/dist/src/lib/workflow/batch-executor.js +232 -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 +345 -220
- 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 +208 -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 +10 -1
- 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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP client detection and configuration
|
|
3
|
+
*
|
|
4
|
+
* Detects installed MCP clients (Claude Desktop, Cursor, VS Code)
|
|
5
|
+
* and generates appropriate configuration entries for Sequant MCP server.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from "fs";
|
|
8
|
+
import * as os from "os";
|
|
9
|
+
import * as path from "path";
|
|
10
|
+
/** Path to the project-level MCP config file used by Claude Code */
|
|
11
|
+
export const PROJECT_MCP_JSON = ".mcp.json";
|
|
12
|
+
/**
|
|
13
|
+
* Clients that need an explicit cwd because they don't run from the project directory.
|
|
14
|
+
*/
|
|
15
|
+
const CLIENTS_NEEDING_CWD = new Set([
|
|
16
|
+
"claude-desktop",
|
|
17
|
+
"vscode-continue",
|
|
18
|
+
]);
|
|
19
|
+
/**
|
|
20
|
+
* Sequant MCP server configuration entry.
|
|
21
|
+
*
|
|
22
|
+
* @param options.projectDir - Absolute project path (used as cwd for clients that need it)
|
|
23
|
+
* @param options.clientType - Target client; determines whether cwd/env are included
|
|
24
|
+
*/
|
|
25
|
+
export function getSequantMcpConfig(options) {
|
|
26
|
+
const config = {
|
|
27
|
+
command: "npx",
|
|
28
|
+
args: ["sequant@latest", "serve"],
|
|
29
|
+
};
|
|
30
|
+
// Add cwd for clients that don't run from the project directory
|
|
31
|
+
if (options?.clientType && CLIENTS_NEEDING_CWD.has(options.clientType)) {
|
|
32
|
+
config.cwd = options.projectDir ?? process.cwd();
|
|
33
|
+
}
|
|
34
|
+
// Only include ANTHROPIC_API_KEY for global client configs (not .mcp.json,
|
|
35
|
+
// which is committed to git and must never contain secrets).
|
|
36
|
+
if (options?.clientType && process.env.ANTHROPIC_API_KEY) {
|
|
37
|
+
config.env = { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY };
|
|
38
|
+
}
|
|
39
|
+
return config;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Detect which MCP-compatible clients are installed
|
|
43
|
+
*/
|
|
44
|
+
export function detectMcpClients() {
|
|
45
|
+
const clients = [];
|
|
46
|
+
const home = os.homedir();
|
|
47
|
+
// Claude Desktop
|
|
48
|
+
const claudeDesktopConfig = process.platform === "darwin"
|
|
49
|
+
? path.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json")
|
|
50
|
+
: process.platform === "win32"
|
|
51
|
+
? path.join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json")
|
|
52
|
+
: path.join(home, ".config", "claude", "claude_desktop_config.json");
|
|
53
|
+
clients.push({
|
|
54
|
+
name: "Claude Desktop",
|
|
55
|
+
clientType: "claude-desktop",
|
|
56
|
+
configPath: claudeDesktopConfig,
|
|
57
|
+
exists: fs.existsSync(claudeDesktopConfig),
|
|
58
|
+
});
|
|
59
|
+
// Cursor
|
|
60
|
+
const cursorConfig = path.join(process.cwd(), ".cursor", "mcp.json");
|
|
61
|
+
clients.push({
|
|
62
|
+
name: "Cursor",
|
|
63
|
+
clientType: "cursor",
|
|
64
|
+
configPath: cursorConfig,
|
|
65
|
+
exists: fs.existsSync(path.join(process.cwd(), ".cursor")),
|
|
66
|
+
});
|
|
67
|
+
// VS Code (Continue extension uses .continue/config.json)
|
|
68
|
+
const vscodeConfig = path.join(home, ".continue", "config.json");
|
|
69
|
+
clients.push({
|
|
70
|
+
name: "VS Code + Continue",
|
|
71
|
+
clientType: "vscode-continue",
|
|
72
|
+
configPath: vscodeConfig,
|
|
73
|
+
exists: fs.existsSync(vscodeConfig),
|
|
74
|
+
});
|
|
75
|
+
return clients;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Add Sequant MCP server to a client's config file.
|
|
79
|
+
* Returns true if written, false if already configured.
|
|
80
|
+
*/
|
|
81
|
+
export function addSequantToMcpConfig(configPath, clientType) {
|
|
82
|
+
const sequantConfig = getSequantMcpConfig({
|
|
83
|
+
projectDir: process.cwd(),
|
|
84
|
+
clientType,
|
|
85
|
+
});
|
|
86
|
+
let config = {};
|
|
87
|
+
if (fs.existsSync(configPath)) {
|
|
88
|
+
try {
|
|
89
|
+
config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Corrupt or empty file — start fresh
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
// Initialize mcpServers if needed (Array.isArray guard: typeof [] === "object")
|
|
96
|
+
if (!config.mcpServers ||
|
|
97
|
+
typeof config.mcpServers !== "object" ||
|
|
98
|
+
Array.isArray(config.mcpServers)) {
|
|
99
|
+
config.mcpServers = {};
|
|
100
|
+
}
|
|
101
|
+
const servers = config.mcpServers;
|
|
102
|
+
// Check if already configured
|
|
103
|
+
if (servers.sequant) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
servers.sequant = sequantConfig;
|
|
107
|
+
// Ensure parent directory exists
|
|
108
|
+
const dir = path.dirname(configPath);
|
|
109
|
+
if (!fs.existsSync(dir)) {
|
|
110
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
111
|
+
}
|
|
112
|
+
fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
113
|
+
return true;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Check whether .mcp.json already has a sequant server entry.
|
|
117
|
+
*/
|
|
118
|
+
export function isSequantInProjectMcpJson(projectDir) {
|
|
119
|
+
const mcpJsonPath = path.resolve(projectDir ?? ".", PROJECT_MCP_JSON);
|
|
120
|
+
if (!fs.existsSync(mcpJsonPath))
|
|
121
|
+
return false;
|
|
122
|
+
try {
|
|
123
|
+
const config = JSON.parse(fs.readFileSync(mcpJsonPath, "utf-8"));
|
|
124
|
+
return !!config?.mcpServers?.sequant;
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Create or update .mcp.json in the project root for Claude Code.
|
|
132
|
+
*
|
|
133
|
+
* - If .mcp.json doesn't exist → create it with the sequant server entry
|
|
134
|
+
* - If .mcp.json exists with a sequant entry → skip (already configured)
|
|
135
|
+
* - If .mcp.json exists without a sequant entry → merge it in
|
|
136
|
+
*
|
|
137
|
+
* Unlike global client configs, .mcp.json does NOT include cwd or env
|
|
138
|
+
* because Claude Code runs from the project root.
|
|
139
|
+
*/
|
|
140
|
+
export function createProjectMcpJson(projectDir) {
|
|
141
|
+
const mcpJsonPath = path.resolve(projectDir ?? ".", PROJECT_MCP_JSON);
|
|
142
|
+
const sequantConfig = getSequantMcpConfig(); // No clientType → no cwd/env
|
|
143
|
+
let config = {};
|
|
144
|
+
let fileExisted = false;
|
|
145
|
+
if (fs.existsSync(mcpJsonPath)) {
|
|
146
|
+
fileExisted = true;
|
|
147
|
+
try {
|
|
148
|
+
config = JSON.parse(fs.readFileSync(mcpJsonPath, "utf-8"));
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
// Corrupt or empty file — start fresh
|
|
152
|
+
config = {};
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// Initialize mcpServers if needed (Array.isArray guard: typeof [] === "object")
|
|
156
|
+
if (!config.mcpServers ||
|
|
157
|
+
typeof config.mcpServers !== "object" ||
|
|
158
|
+
Array.isArray(config.mcpServers)) {
|
|
159
|
+
config.mcpServers = {};
|
|
160
|
+
}
|
|
161
|
+
const servers = config.mcpServers;
|
|
162
|
+
// Already configured — skip
|
|
163
|
+
if (servers.sequant) {
|
|
164
|
+
return { created: false, merged: false, skipped: true };
|
|
165
|
+
}
|
|
166
|
+
servers.sequant = sequantConfig;
|
|
167
|
+
fs.writeFileSync(mcpJsonPath, JSON.stringify(config, null, 2) + "\n");
|
|
168
|
+
if (fileExisted) {
|
|
169
|
+
return { created: false, merged: true, skipped: false };
|
|
170
|
+
}
|
|
171
|
+
return { created: true, merged: false, skipped: false };
|
|
172
|
+
}
|
|
@@ -8,6 +8,7 @@ import * as fs from "fs";
|
|
|
8
8
|
import * as path from "path";
|
|
9
9
|
import * as os from "os";
|
|
10
10
|
import { spawnSync } from "child_process";
|
|
11
|
+
import { GitHubProvider } from "../workflow/platforms/github.js";
|
|
11
12
|
import { RunLogSchema, LOG_PATHS, } from "../workflow/run-log-schema.js";
|
|
12
13
|
import { getGitDiffStats } from "../workflow/git-diff-utils.js";
|
|
13
14
|
import { DEFAULT_MIRROR_PAIRS } from "./types.js";
|
|
@@ -125,7 +126,9 @@ export function resolveBranches(issueNumbers, repoRoot, runLog) {
|
|
|
125
126
|
: [];
|
|
126
127
|
}
|
|
127
128
|
const info = issueInfo.get(issueNumber);
|
|
128
|
-
const title = info?.title ??
|
|
129
|
+
const title = info?.title ??
|
|
130
|
+
ghProvider.fetchIssueTitleSync(String(issueNumber)) ??
|
|
131
|
+
`Issue #${issueNumber}`;
|
|
129
132
|
branches.push({
|
|
130
133
|
issueNumber,
|
|
131
134
|
title,
|
|
@@ -137,17 +140,8 @@ export function resolveBranches(issueNumbers, repoRoot, runLog) {
|
|
|
137
140
|
}
|
|
138
141
|
return branches;
|
|
139
142
|
}
|
|
140
|
-
/**
|
|
141
|
-
|
|
142
|
-
* Returns null if gh is not available or the issue doesn't exist.
|
|
143
|
-
*/
|
|
144
|
-
function fetchIssueTitle(issueNumber) {
|
|
145
|
-
const result = spawnSync("gh", ["issue", "view", String(issueNumber), "--json", "title", "--jq", ".title"], { stdio: "pipe", encoding: "utf-8", timeout: 10_000 });
|
|
146
|
-
if (result.status !== 0 || !result.stdout?.trim()) {
|
|
147
|
-
return null;
|
|
148
|
-
}
|
|
149
|
-
return result.stdout.trim();
|
|
150
|
-
}
|
|
143
|
+
/** Shared GitHubProvider instance for issue title lookups. */
|
|
144
|
+
const ghProvider = new GitHubProvider();
|
|
151
145
|
/**
|
|
152
146
|
* Determine which checks to run based on command options.
|
|
153
147
|
*
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Shared type definitions for batch-level integration QA checks.
|
|
5
5
|
*/
|
|
6
|
+
import { z } from "zod";
|
|
6
7
|
/**
|
|
7
8
|
* Information about a feature branch in a batch
|
|
8
9
|
*/
|
|
@@ -21,13 +22,25 @@ export interface BranchInfo {
|
|
|
21
22
|
filesModified: string[];
|
|
22
23
|
}
|
|
23
24
|
/**
|
|
24
|
-
* Per-issue verdict from a check
|
|
25
|
-
*/
|
|
26
|
-
export
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
25
|
+
* Per-issue verdict from a check (Zod schema + inferred type)
|
|
26
|
+
*/
|
|
27
|
+
export declare const CHECK_VERDICTS: readonly ["PASS", "WARN", "FAIL"];
|
|
28
|
+
export declare const CheckVerdictSchema: z.ZodEnum<{
|
|
29
|
+
PASS: "PASS";
|
|
30
|
+
WARN: "WARN";
|
|
31
|
+
FAIL: "FAIL";
|
|
32
|
+
}>;
|
|
33
|
+
export type CheckVerdict = z.infer<typeof CheckVerdictSchema>;
|
|
34
|
+
/**
|
|
35
|
+
* Batch-level verdict (Zod schema + inferred type)
|
|
36
|
+
*/
|
|
37
|
+
export declare const BATCH_VERDICTS: readonly ["READY", "NEEDS_ATTENTION", "BLOCKED"];
|
|
38
|
+
export declare const BatchVerdictSchema: z.ZodEnum<{
|
|
39
|
+
READY: "READY";
|
|
40
|
+
NEEDS_ATTENTION: "NEEDS_ATTENTION";
|
|
41
|
+
BLOCKED: "BLOCKED";
|
|
42
|
+
}>;
|
|
43
|
+
export type BatchVerdict = z.infer<typeof BatchVerdictSchema>;
|
|
31
44
|
/**
|
|
32
45
|
* A single finding from a check
|
|
33
46
|
*/
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Shared type definitions for batch-level integration QA checks.
|
|
5
5
|
*/
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
/**
|
|
8
|
+
* Per-issue verdict from a check (Zod schema + inferred type)
|
|
9
|
+
*/
|
|
10
|
+
export const CHECK_VERDICTS = ["PASS", "WARN", "FAIL"];
|
|
11
|
+
export const CheckVerdictSchema = z.enum(CHECK_VERDICTS);
|
|
12
|
+
/**
|
|
13
|
+
* Batch-level verdict (Zod schema + inferred type)
|
|
14
|
+
*/
|
|
15
|
+
export const BATCH_VERDICTS = ["READY", "NEEDS_ATTENTION", "BLOCKED"];
|
|
16
|
+
export const BatchVerdictSchema = z.enum(BATCH_VERDICTS);
|
|
6
17
|
/**
|
|
7
18
|
* Default mirror pairs for this project
|
|
8
19
|
*/
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides types for tracking where phase recommendations come from
|
|
5
5
|
* and logic for merging signals with priority:
|
|
6
|
-
* Labels >
|
|
6
|
+
* Labels > Assess Comment > Title > Body
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```typescript
|
|
@@ -23,7 +23,7 @@ import type { Phase } from "./workflow/types.js";
|
|
|
23
23
|
/**
|
|
24
24
|
* Source of a phase signal, ordered by priority (highest first)
|
|
25
25
|
*/
|
|
26
|
-
export type SignalSource = "label" | "solve" | "title" | "body";
|
|
26
|
+
export type SignalSource = "label" | "assess" | "solve" | "title" | "body";
|
|
27
27
|
/**
|
|
28
28
|
* Priority order for signal sources (higher = takes precedence)
|
|
29
29
|
*/
|
|
@@ -63,7 +63,7 @@ export interface MergedPhaseResult {
|
|
|
63
63
|
/**
|
|
64
64
|
* Merge phase signals with priority-based deduplication
|
|
65
65
|
*
|
|
66
|
-
* Priority order: Labels >
|
|
66
|
+
* Priority order: Labels > Assess > Title > Body
|
|
67
67
|
* When multiple signals suggest the same phase, the highest-priority
|
|
68
68
|
* source wins. Signals can only ADD phases, never remove.
|
|
69
69
|
*
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Provides types for tracking where phase recommendations come from
|
|
5
5
|
* and logic for merging signals with priority:
|
|
6
|
-
* Labels >
|
|
6
|
+
* Labels > Assess Comment > Title > Body
|
|
7
7
|
*
|
|
8
8
|
* @example
|
|
9
9
|
* ```typescript
|
|
@@ -24,14 +24,16 @@
|
|
|
24
24
|
*/
|
|
25
25
|
export const SIGNAL_PRIORITY = {
|
|
26
26
|
label: 4, // Highest priority - explicit labels
|
|
27
|
-
|
|
27
|
+
assess: 3, // Assess command analysis
|
|
28
|
+
/** @deprecated Use `assess` instead */
|
|
29
|
+
solve: 3, // Solve command analysis (backward compat)
|
|
28
30
|
title: 2, // Title keyword detection
|
|
29
31
|
body: 1, // Body pattern detection (lowest)
|
|
30
32
|
};
|
|
31
33
|
/**
|
|
32
34
|
* Merge phase signals with priority-based deduplication
|
|
33
35
|
*
|
|
34
|
-
* Priority order: Labels >
|
|
36
|
+
* Priority order: Labels > Assess > Title > Body
|
|
35
37
|
* When multiple signals suggest the same phase, the highest-priority
|
|
36
38
|
* source wins. Signals can only ADD phases, never remove.
|
|
37
39
|
*
|
|
@@ -45,6 +45,17 @@ export interface AgentSettings {
|
|
|
45
45
|
*/
|
|
46
46
|
model: "haiku" | "sonnet" | "opus";
|
|
47
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Aider-specific settings for the aider agent driver.
|
|
50
|
+
*/
|
|
51
|
+
export interface AiderSettings {
|
|
52
|
+
/** Model to use (e.g., "claude-3-sonnet", "gpt-4o") */
|
|
53
|
+
model?: string;
|
|
54
|
+
/** Edit format (e.g., "diff", "whole", "udiff") */
|
|
55
|
+
editFormat?: string;
|
|
56
|
+
/** Extra CLI arguments passed to aider */
|
|
57
|
+
extraArgs?: string[];
|
|
58
|
+
}
|
|
48
59
|
/**
|
|
49
60
|
* Run command settings
|
|
50
61
|
*/
|
|
@@ -59,6 +70,8 @@ export interface RunSettings {
|
|
|
59
70
|
timeout: number;
|
|
60
71
|
/** Run issues sequentially by default */
|
|
61
72
|
sequential: boolean;
|
|
73
|
+
/** Max concurrent issues in parallel mode (default: 3) */
|
|
74
|
+
concurrency: number;
|
|
62
75
|
/** Enable quality loop by default */
|
|
63
76
|
qualityLoop: boolean;
|
|
64
77
|
/** Max iterations for quality loop */
|
|
@@ -95,6 +108,23 @@ export interface RunSettings {
|
|
|
95
108
|
* Default: 5
|
|
96
109
|
*/
|
|
97
110
|
staleBranchThreshold: number;
|
|
111
|
+
/**
|
|
112
|
+
* TTL in days for resolved issues on the dashboard.
|
|
113
|
+
* After this period, resolved issues are auto-pruned on next read.
|
|
114
|
+
* - Default: 7 (one week)
|
|
115
|
+
* - 0: Never auto-prune (manual cleanup only)
|
|
116
|
+
* - -1: Prune immediately (resolved issues never shown)
|
|
117
|
+
*/
|
|
118
|
+
resolvedIssueTTL: number;
|
|
119
|
+
/**
|
|
120
|
+
* Agent driver for phase execution.
|
|
121
|
+
* Default: "claude-code". Set to "aider" to use Aider CLI.
|
|
122
|
+
*/
|
|
123
|
+
agent?: string;
|
|
124
|
+
/**
|
|
125
|
+
* Aider-specific configuration. Only used when agent is "aider".
|
|
126
|
+
*/
|
|
127
|
+
aider?: AiderSettings;
|
|
98
128
|
}
|
|
99
129
|
/**
|
|
100
130
|
* Scope assessment threshold configuration
|
|
@@ -151,6 +181,17 @@ export interface ScopeAssessmentSettings {
|
|
|
151
181
|
directorySpread: ScopeThreshold;
|
|
152
182
|
};
|
|
153
183
|
}
|
|
184
|
+
/**
|
|
185
|
+
* QA skill settings
|
|
186
|
+
*/
|
|
187
|
+
export interface QASettings {
|
|
188
|
+
/**
|
|
189
|
+
* Diff size threshold (additions + deletions) for the small-diff fast path.
|
|
190
|
+
* Diffs below this threshold skip sub-agent spawning and use inline checks.
|
|
191
|
+
* Default: 100
|
|
192
|
+
*/
|
|
193
|
+
smallDiffThreshold: number;
|
|
194
|
+
}
|
|
154
195
|
/**
|
|
155
196
|
* Full settings schema
|
|
156
197
|
*/
|
|
@@ -163,6 +204,8 @@ export interface SequantSettings {
|
|
|
163
204
|
agents: AgentSettings;
|
|
164
205
|
/** Scope assessment settings */
|
|
165
206
|
scopeAssessment: ScopeAssessmentSettings;
|
|
207
|
+
/** QA skill settings */
|
|
208
|
+
qa: QASettings;
|
|
166
209
|
}
|
|
167
210
|
/**
|
|
168
211
|
* Default rotation settings
|
|
@@ -186,10 +229,19 @@ export declare const DEFAULT_TRIVIAL_THRESHOLDS: TrivialThresholds;
|
|
|
186
229
|
* src/lib/scope/types.ts to ensure consistency.
|
|
187
230
|
*/
|
|
188
231
|
export declare const DEFAULT_SCOPE_ASSESSMENT_SETTINGS: ScopeAssessmentSettings;
|
|
232
|
+
/**
|
|
233
|
+
* Default QA settings
|
|
234
|
+
*/
|
|
235
|
+
export declare const DEFAULT_QA_SETTINGS: QASettings;
|
|
189
236
|
/**
|
|
190
237
|
* Default settings
|
|
191
238
|
*/
|
|
192
239
|
export declare const DEFAULT_SETTINGS: SequantSettings;
|
|
240
|
+
/**
|
|
241
|
+
* Validate aider-specific settings.
|
|
242
|
+
* Throws on invalid types to catch config errors at load time.
|
|
243
|
+
*/
|
|
244
|
+
export declare function validateAiderSettings(aider: unknown): AiderSettings | undefined;
|
|
193
245
|
/**
|
|
194
246
|
* Get the current project settings
|
|
195
247
|
*
|
package/dist/src/lib/settings.js
CHANGED
|
@@ -68,6 +68,12 @@ export const DEFAULT_SCOPE_ASSESSMENT_SETTINGS = {
|
|
|
68
68
|
directorySpread: { yellow: 3, red: 5 },
|
|
69
69
|
},
|
|
70
70
|
};
|
|
71
|
+
/**
|
|
72
|
+
* Default QA settings
|
|
73
|
+
*/
|
|
74
|
+
export const DEFAULT_QA_SETTINGS = {
|
|
75
|
+
smallDiffThreshold: 100,
|
|
76
|
+
};
|
|
71
77
|
/**
|
|
72
78
|
* Default settings
|
|
73
79
|
*/
|
|
@@ -79,6 +85,7 @@ export const DEFAULT_SETTINGS = {
|
|
|
79
85
|
autoDetectPhases: true,
|
|
80
86
|
timeout: 1800,
|
|
81
87
|
sequential: false,
|
|
88
|
+
concurrency: 3,
|
|
82
89
|
qualityLoop: false,
|
|
83
90
|
maxIterations: 3,
|
|
84
91
|
smartTests: true,
|
|
@@ -86,10 +93,37 @@ export const DEFAULT_SETTINGS = {
|
|
|
86
93
|
mcp: true, // Enable MCP servers by default in headless mode
|
|
87
94
|
retry: true, // Enable automatic retry with MCP fallback by default
|
|
88
95
|
staleBranchThreshold: 5, // Block QA/test if feature is >5 commits behind main
|
|
96
|
+
resolvedIssueTTL: 7, // Auto-prune resolved issues after 7 days
|
|
89
97
|
},
|
|
90
98
|
agents: DEFAULT_AGENT_SETTINGS,
|
|
91
99
|
scopeAssessment: DEFAULT_SCOPE_ASSESSMENT_SETTINGS,
|
|
100
|
+
qa: DEFAULT_QA_SETTINGS,
|
|
92
101
|
};
|
|
102
|
+
/**
|
|
103
|
+
* Validate aider-specific settings.
|
|
104
|
+
* Throws on invalid types to catch config errors at load time.
|
|
105
|
+
*/
|
|
106
|
+
export function validateAiderSettings(aider) {
|
|
107
|
+
if (aider == null)
|
|
108
|
+
return undefined;
|
|
109
|
+
if (typeof aider !== "object" || Array.isArray(aider)) {
|
|
110
|
+
throw new Error("settings.run.aider must be an object");
|
|
111
|
+
}
|
|
112
|
+
const obj = aider;
|
|
113
|
+
if (obj.model !== undefined && typeof obj.model !== "string") {
|
|
114
|
+
throw new Error("settings.run.aider.model must be a string");
|
|
115
|
+
}
|
|
116
|
+
if (obj.editFormat !== undefined && typeof obj.editFormat !== "string") {
|
|
117
|
+
throw new Error("settings.run.aider.editFormat must be a string");
|
|
118
|
+
}
|
|
119
|
+
if (obj.extraArgs !== undefined) {
|
|
120
|
+
if (!Array.isArray(obj.extraArgs) ||
|
|
121
|
+
!obj.extraArgs.every((a) => typeof a === "string")) {
|
|
122
|
+
throw new Error("settings.run.aider.extraArgs must be an array of strings");
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return obj;
|
|
126
|
+
}
|
|
93
127
|
/**
|
|
94
128
|
* Get the current project settings
|
|
95
129
|
*
|
|
@@ -102,12 +136,15 @@ export async function getSettings() {
|
|
|
102
136
|
try {
|
|
103
137
|
const content = await readFile(SETTINGS_PATH);
|
|
104
138
|
const parsed = JSON.parse(content);
|
|
139
|
+
// Validate aider settings if present
|
|
140
|
+
const aiderSettings = validateAiderSettings(parsed.run?.aider);
|
|
105
141
|
// Merge with defaults to ensure all fields exist
|
|
106
142
|
return {
|
|
107
143
|
version: parsed.version ?? DEFAULT_SETTINGS.version,
|
|
108
144
|
run: {
|
|
109
145
|
...DEFAULT_SETTINGS.run,
|
|
110
146
|
...parsed.run,
|
|
147
|
+
...(aiderSettings !== undefined ? { aider: aiderSettings } : {}),
|
|
111
148
|
},
|
|
112
149
|
agents: {
|
|
113
150
|
...DEFAULT_AGENT_SETTINGS,
|
|
@@ -125,6 +162,10 @@ export async function getSettings() {
|
|
|
125
162
|
...parsed.scopeAssessment?.thresholds,
|
|
126
163
|
},
|
|
127
164
|
},
|
|
165
|
+
qa: {
|
|
166
|
+
...DEFAULT_QA_SETTINGS,
|
|
167
|
+
...parsed.qa,
|
|
168
|
+
},
|
|
128
169
|
};
|
|
129
170
|
}
|
|
130
171
|
catch {
|
|
@@ -46,7 +46,8 @@ export interface ShutdownManagerOptions {
|
|
|
46
46
|
export declare class ShutdownManager {
|
|
47
47
|
private cleanupTasks;
|
|
48
48
|
private _isShuttingDown;
|
|
49
|
-
|
|
49
|
+
/** Active abort controllers — supports concurrent phase execution (#404) */
|
|
50
|
+
private abortControllers;
|
|
50
51
|
private forceExitTimeout;
|
|
51
52
|
private output;
|
|
52
53
|
private errorOutput;
|
|
@@ -63,14 +64,24 @@ export declare class ShutdownManager {
|
|
|
63
64
|
*/
|
|
64
65
|
get shuttingDown(): boolean;
|
|
65
66
|
/**
|
|
66
|
-
*
|
|
67
|
+
* Register an abort controller for a running phase.
|
|
67
68
|
*
|
|
68
|
-
* When shutdown is triggered,
|
|
69
|
-
*
|
|
69
|
+
* When shutdown is triggered, ALL registered controllers are aborted,
|
|
70
|
+
* supporting concurrent phase execution.
|
|
71
|
+
*/
|
|
72
|
+
addAbortController(controller: AbortController): void;
|
|
73
|
+
/**
|
|
74
|
+
* Unregister a specific abort controller after a phase completes.
|
|
75
|
+
*
|
|
76
|
+
* Only removes the given controller — others remain active.
|
|
77
|
+
*/
|
|
78
|
+
removeAbortController(controller: AbortController): void;
|
|
79
|
+
/**
|
|
80
|
+
* @deprecated Use addAbortController/removeAbortController for concurrent safety.
|
|
70
81
|
*/
|
|
71
82
|
setAbortController(controller: AbortController): void;
|
|
72
83
|
/**
|
|
73
|
-
*
|
|
84
|
+
* @deprecated Use removeAbortController(controller) instead.
|
|
74
85
|
*/
|
|
75
86
|
clearAbortController(): void;
|
|
76
87
|
/**
|
package/dist/src/lib/shutdown.js
CHANGED
|
@@ -34,7 +34,8 @@ import chalk from "chalk";
|
|
|
34
34
|
export class ShutdownManager {
|
|
35
35
|
cleanupTasks = [];
|
|
36
36
|
_isShuttingDown = false;
|
|
37
|
-
|
|
37
|
+
/** Active abort controllers — supports concurrent phase execution (#404) */
|
|
38
|
+
abortControllers = new Set();
|
|
38
39
|
forceExitTimeout;
|
|
39
40
|
output;
|
|
40
41
|
errorOutput;
|
|
@@ -67,19 +68,34 @@ export class ShutdownManager {
|
|
|
67
68
|
return this._isShuttingDown;
|
|
68
69
|
}
|
|
69
70
|
/**
|
|
70
|
-
*
|
|
71
|
+
* Register an abort controller for a running phase.
|
|
71
72
|
*
|
|
72
|
-
* When shutdown is triggered,
|
|
73
|
-
*
|
|
73
|
+
* When shutdown is triggered, ALL registered controllers are aborted,
|
|
74
|
+
* supporting concurrent phase execution.
|
|
75
|
+
*/
|
|
76
|
+
addAbortController(controller) {
|
|
77
|
+
this.abortControllers.add(controller);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Unregister a specific abort controller after a phase completes.
|
|
81
|
+
*
|
|
82
|
+
* Only removes the given controller — others remain active.
|
|
83
|
+
*/
|
|
84
|
+
removeAbortController(controller) {
|
|
85
|
+
this.abortControllers.delete(controller);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* @deprecated Use addAbortController/removeAbortController for concurrent safety.
|
|
74
89
|
*/
|
|
75
90
|
setAbortController(controller) {
|
|
76
|
-
this.
|
|
91
|
+
this.abortControllers.clear();
|
|
92
|
+
this.abortControllers.add(controller);
|
|
77
93
|
}
|
|
78
94
|
/**
|
|
79
|
-
*
|
|
95
|
+
* @deprecated Use removeAbortController(controller) instead.
|
|
80
96
|
*/
|
|
81
97
|
clearAbortController() {
|
|
82
|
-
this.
|
|
98
|
+
this.abortControllers.clear();
|
|
83
99
|
}
|
|
84
100
|
/**
|
|
85
101
|
* Register a cleanup task
|
|
@@ -123,10 +139,14 @@ export class ShutdownManager {
|
|
|
123
139
|
}
|
|
124
140
|
this._isShuttingDown = true;
|
|
125
141
|
this.output(chalk.yellow(`\n⚠️ Received ${signal}, shutting down gracefully...`));
|
|
126
|
-
// Abort in-flight
|
|
127
|
-
if (this.
|
|
128
|
-
this.
|
|
129
|
-
|
|
142
|
+
// Abort all in-flight phases immediately
|
|
143
|
+
if (this.abortControllers.size > 0) {
|
|
144
|
+
const count = this.abortControllers.size;
|
|
145
|
+
for (const controller of this.abortControllers) {
|
|
146
|
+
controller.abort();
|
|
147
|
+
}
|
|
148
|
+
this.abortControllers.clear();
|
|
149
|
+
this.output(chalk.green(`✓ Aborted ${count} active phase${count > 1 ? "s" : ""}`));
|
|
130
150
|
}
|
|
131
151
|
// Set up force exit timeout
|
|
132
152
|
const forceExitTimer = setTimeout(() => {
|
|
@@ -160,7 +180,7 @@ export class ShutdownManager {
|
|
|
160
180
|
process.removeListener("SIGINT", this.sigintHandler);
|
|
161
181
|
process.removeListener("SIGTERM", this.sigtermHandler);
|
|
162
182
|
this.cleanupTasks = [];
|
|
163
|
-
this.
|
|
183
|
+
this.abortControllers.clear();
|
|
164
184
|
}
|
|
165
185
|
}
|
|
166
186
|
/**
|