sequant 1.14.1 → 1.15.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/plugin.json +1 -1
- package/README.md +1 -0
- package/dist/bin/cli.js +17 -1
- package/dist/src/commands/run.js +34 -3
- package/dist/src/commands/status.js +23 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -223,6 +223,7 @@ See [Customization Guide](docs/guides/customization.md) for all options.
|
|
|
223
223
|
- [Complete Workflow](docs/guides/workflow.md) — Full workflow including post-QA patterns
|
|
224
224
|
- [Getting Started](docs/getting-started/installation.md)
|
|
225
225
|
- [What We've Built](docs/internal/what-weve-built.md) — Comprehensive project overview
|
|
226
|
+
- [What Is Sequant](docs/concepts/what-is-sequant.md) — Elevator pitch, pipeline diagram, architecture
|
|
226
227
|
- [Workflow Concepts](docs/concepts/workflow-phases.md)
|
|
227
228
|
- [Run Command](docs/reference/run-command.md)
|
|
228
229
|
- [Git Workflows](docs/guides/git-workflows.md)
|
package/dist/bin/cli.js
CHANGED
|
@@ -43,7 +43,8 @@ import { logsCommand } from "../src/commands/logs.js";
|
|
|
43
43
|
import { statsCommand } from "../src/commands/stats.js";
|
|
44
44
|
import { dashboardCommand } from "../src/commands/dashboard.js";
|
|
45
45
|
import { stateInitCommand, stateRebuildCommand, stateCleanCommand, } from "../src/commands/state.js";
|
|
46
|
-
import { syncCommand } from "../src/commands/sync.js";
|
|
46
|
+
import { syncCommand, areSkillsOutdated } from "../src/commands/sync.js";
|
|
47
|
+
import { getManifest } from "../src/lib/manifest.js";
|
|
47
48
|
const program = new Command();
|
|
48
49
|
// Handle --no-color before parsing
|
|
49
50
|
if (process.argv.includes("--no-color")) {
|
|
@@ -191,6 +192,21 @@ stateCmd
|
|
|
191
192
|
.option("--max-age <days>", "Remove entries older than N days", parseInt)
|
|
192
193
|
.option("--all", "Remove all orphaned entries (merged and abandoned)")
|
|
193
194
|
.action(stateCleanCommand);
|
|
195
|
+
// Auto-sync skills after npm upgrade (version mismatch detection)
|
|
196
|
+
// Only triggers when skills were previously synced (has .sequant-version marker).
|
|
197
|
+
// Projects that manage skills manually (no marker) are not affected.
|
|
198
|
+
program.hook("preAction", async (thisCommand) => {
|
|
199
|
+
const cmd = thisCommand.name();
|
|
200
|
+
if (cmd === "init" || cmd === "sync")
|
|
201
|
+
return;
|
|
202
|
+
const manifest = await getManifest();
|
|
203
|
+
if (!manifest)
|
|
204
|
+
return;
|
|
205
|
+
const { outdated, currentVersion } = await areSkillsOutdated();
|
|
206
|
+
if (outdated && currentVersion !== null) {
|
|
207
|
+
await syncCommand({ quiet: true });
|
|
208
|
+
}
|
|
209
|
+
});
|
|
194
210
|
// Parse and execute
|
|
195
211
|
program.parse();
|
|
196
212
|
// Show help if no command provided
|
package/dist/src/commands/run.js
CHANGED
|
@@ -835,6 +835,37 @@ async function executePhase(issueNumber, phase, config, sessionId, worktreePath,
|
|
|
835
835
|
};
|
|
836
836
|
}
|
|
837
837
|
}
|
|
838
|
+
/**
|
|
839
|
+
* Cold-start retry threshold in seconds.
|
|
840
|
+
* Failures under this duration are likely Claude Code subprocess initialization
|
|
841
|
+
* issues rather than genuine phase failures (based on empirical data: cold-start
|
|
842
|
+
* failures consistently complete in 15-39s vs 150-310s for real work).
|
|
843
|
+
*/
|
|
844
|
+
const COLD_START_THRESHOLD_SECONDS = 60;
|
|
845
|
+
const COLD_START_MAX_RETRIES = 2;
|
|
846
|
+
/**
|
|
847
|
+
* Execute a phase with automatic retry for cold-start failures.
|
|
848
|
+
* If a phase fails within COLD_START_THRESHOLD_SECONDS, it's likely a subprocess
|
|
849
|
+
* initialization issue — retry up to COLD_START_MAX_RETRIES times before giving up.
|
|
850
|
+
*/
|
|
851
|
+
async function executePhaseWithRetry(issueNumber, phase, config, sessionId, worktreePath, shutdownManager, spinner) {
|
|
852
|
+
let lastResult;
|
|
853
|
+
for (let attempt = 0; attempt <= COLD_START_MAX_RETRIES; attempt++) {
|
|
854
|
+
lastResult = await executePhase(issueNumber, phase, config, sessionId, worktreePath, shutdownManager, spinner);
|
|
855
|
+
const duration = lastResult.durationSeconds ?? 0;
|
|
856
|
+
// Success or genuine failure (took long enough to be real work)
|
|
857
|
+
if (lastResult.success || duration >= COLD_START_THRESHOLD_SECONDS) {
|
|
858
|
+
return lastResult;
|
|
859
|
+
}
|
|
860
|
+
// Cold-start failure detected — retry
|
|
861
|
+
if (attempt < COLD_START_MAX_RETRIES) {
|
|
862
|
+
if (config.verbose) {
|
|
863
|
+
console.log(chalk.yellow(`\n ⟳ Cold-start failure detected (${duration.toFixed(1)}s), retrying... (attempt ${attempt + 2}/${COLD_START_MAX_RETRIES + 1})`));
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return lastResult;
|
|
868
|
+
}
|
|
838
869
|
/**
|
|
839
870
|
* Fetch issue info from GitHub
|
|
840
871
|
*/
|
|
@@ -1575,7 +1606,7 @@ async function runIssueWithLogging(issueNumber, config, logWriter, stateManager,
|
|
|
1575
1606
|
}
|
|
1576
1607
|
const specStartTime = new Date();
|
|
1577
1608
|
// Note: spec runs in main repo (not worktree) for planning
|
|
1578
|
-
const specResult = await
|
|
1609
|
+
const specResult = await executePhaseWithRetry(issueNumber, "spec", config, sessionId, worktreePath, // Will be ignored for spec (non-isolated phase)
|
|
1579
1610
|
shutdownManager, specSpinner);
|
|
1580
1611
|
const specEndTime = new Date();
|
|
1581
1612
|
if (specResult.sessionId) {
|
|
@@ -1718,7 +1749,7 @@ async function runIssueWithLogging(issueNumber, config, logWriter, stateManager,
|
|
|
1718
1749
|
}
|
|
1719
1750
|
}
|
|
1720
1751
|
const phaseStartTime = new Date();
|
|
1721
|
-
const result = await
|
|
1752
|
+
const result = await executePhaseWithRetry(issueNumber, phase, config, sessionId, worktreePath, shutdownManager, phaseSpinner);
|
|
1722
1753
|
const phaseEndTime = new Date();
|
|
1723
1754
|
// Capture session ID for subsequent phases
|
|
1724
1755
|
if (result.sessionId) {
|
|
@@ -1778,7 +1809,7 @@ async function runIssueWithLogging(issueNumber, config, logWriter, stateManager,
|
|
|
1778
1809
|
iteration,
|
|
1779
1810
|
});
|
|
1780
1811
|
loopSpinner.start();
|
|
1781
|
-
const loopResult = await
|
|
1812
|
+
const loopResult = await executePhaseWithRetry(issueNumber, "loop", config, sessionId, worktreePath, shutdownManager, loopSpinner);
|
|
1782
1813
|
phaseResults.push(loopResult);
|
|
1783
1814
|
if (loopResult.sessionId) {
|
|
1784
1815
|
sessionId = loopResult.sessionId;
|
|
@@ -7,7 +7,23 @@ import { getManifest, getPackageVersion } from "../lib/manifest.js";
|
|
|
7
7
|
import { fileExists } from "../lib/fs.js";
|
|
8
8
|
import { readdir } from "fs/promises";
|
|
9
9
|
import { StateManager } from "../lib/workflow/state-manager.js";
|
|
10
|
-
import { rebuildStateFromLogs, cleanupStaleEntries, } from "../lib/workflow/state-utils.js";
|
|
10
|
+
import { rebuildStateFromLogs, cleanupStaleEntries, checkPRMergeStatus, } from "../lib/workflow/state-utils.js";
|
|
11
|
+
/**
|
|
12
|
+
* Auto-detect merged PRs and update state for issues stuck at ready_for_merge.
|
|
13
|
+
* Queries GitHub for each ready_for_merge issue that has a PR number.
|
|
14
|
+
*/
|
|
15
|
+
async function refreshMergedStatuses(stateManager, issues) {
|
|
16
|
+
const readyIssues = issues.filter((i) => i.status === "ready_for_merge" && i.pr?.number);
|
|
17
|
+
if (readyIssues.length === 0)
|
|
18
|
+
return;
|
|
19
|
+
for (const issue of readyIssues) {
|
|
20
|
+
const prStatus = checkPRMergeStatus(issue.pr.number);
|
|
21
|
+
if (prStatus === "MERGED") {
|
|
22
|
+
issue.status = "merged";
|
|
23
|
+
await stateManager.updateIssueStatus(issue.number, "merged");
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
11
27
|
/**
|
|
12
28
|
* Color-code issue status
|
|
13
29
|
*/
|
|
@@ -220,7 +236,7 @@ export async function statusCommand(options = {}) {
|
|
|
220
236
|
return;
|
|
221
237
|
}
|
|
222
238
|
console.log(chalk.green("Status: Initialized"));
|
|
223
|
-
console.log(chalk.gray(`
|
|
239
|
+
console.log(chalk.gray(`Skills version: ${manifest.version}`));
|
|
224
240
|
console.log(chalk.gray(`Stack: ${manifest.stack}`));
|
|
225
241
|
console.log(chalk.gray(`Installed: ${manifest.installedAt}`));
|
|
226
242
|
if (manifest.updatedAt) {
|
|
@@ -250,6 +266,7 @@ export async function statusCommand(options = {}) {
|
|
|
250
266
|
const allIssues = await stateManager.getAllIssueStates();
|
|
251
267
|
const issues = Object.values(allIssues);
|
|
252
268
|
if (issues.length > 0) {
|
|
269
|
+
await refreshMergedStatuses(stateManager, issues);
|
|
253
270
|
displayIssueSummary(issues);
|
|
254
271
|
}
|
|
255
272
|
}
|
|
@@ -279,6 +296,9 @@ async function displayIssueState(options) {
|
|
|
279
296
|
if (options.issue !== undefined) {
|
|
280
297
|
// Show single issue details
|
|
281
298
|
const issueState = await stateManager.getIssueState(options.issue);
|
|
299
|
+
if (issueState) {
|
|
300
|
+
await refreshMergedStatuses(stateManager, [issueState]);
|
|
301
|
+
}
|
|
282
302
|
if (options.json) {
|
|
283
303
|
console.log(JSON.stringify(issueState, null, 2));
|
|
284
304
|
}
|
|
@@ -294,6 +314,7 @@ async function displayIssueState(options) {
|
|
|
294
314
|
// Show all issues
|
|
295
315
|
const allIssues = await stateManager.getAllIssueStates();
|
|
296
316
|
const issues = Object.values(allIssues);
|
|
317
|
+
await refreshMergedStatuses(stateManager, issues);
|
|
297
318
|
if (options.json) {
|
|
298
319
|
console.log(JSON.stringify({ issues: allIssues }, null, 2));
|
|
299
320
|
}
|