sequant 2.6.0 → 2.6.2
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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +5 -0
- package/dist/bin/cli.js +27 -4
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/clean/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/improve/SKILL.md +4 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/security-review/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/solve/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +3 -0
- package/dist/marketplace/external_plugins/sequant/skills/verify/SKILL.md +3 -0
- package/dist/src/commands/ready.js +1 -1
- package/dist/src/commands/run.js +1 -1
- package/dist/src/commands/sync.d.ts +43 -5
- package/dist/src/commands/sync.js +188 -17
- package/dist/src/commands/update.d.ts +1 -0
- package/dist/src/commands/update.js +73 -68
- package/dist/src/lib/templates.d.ts +50 -0
- package/dist/src/lib/templates.js +134 -15
- package/dist/src/ui/tui/App.js +24 -2
- package/dist/src/ui/tui/IssueBox.js +4 -4
- package/dist/src/ui/tui/load.d.ts +25 -0
- package/dist/src/ui/tui/load.js +41 -0
- package/dist/src/ui/tui/theme.d.ts +21 -3
- package/dist/src/ui/tui/theme.js +22 -4
- package/package.json +1 -1
- package/templates/skills/assess/SKILL.md +3 -0
- package/templates/skills/clean/SKILL.md +3 -0
- package/templates/skills/docs/SKILL.md +3 -0
- package/templates/skills/exec/SKILL.md +3 -0
- package/templates/skills/fullsolve/SKILL.md +3 -0
- package/templates/skills/improve/SKILL.md +4 -1
- package/templates/skills/loop/SKILL.md +3 -0
- package/templates/skills/merger/SKILL.md +3 -0
- package/templates/skills/qa/SKILL.md +3 -0
- package/templates/skills/reflect/SKILL.md +3 -0
- package/templates/skills/security-review/SKILL.md +3 -0
- package/templates/skills/setup/SKILL.md +3 -0
- package/templates/skills/solve/SKILL.md +3 -0
- package/templates/skills/spec/SKILL.md +3 -0
- package/templates/skills/test/SKILL.md +3 -0
- package/templates/skills/testgen/SKILL.md +3 -0
- package/templates/skills/verify/SKILL.md +3 -0
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
{
|
|
9
9
|
"name": "sequant",
|
|
10
10
|
"description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees, quality gates, and an MCP server. Includes 17 skills, workflow MCP tools, and pre/post-tool hooks.",
|
|
11
|
-
"version": "2.6.
|
|
11
|
+
"version": "2.6.2",
|
|
12
12
|
"author": {
|
|
13
13
|
"name": "sequant-io",
|
|
14
14
|
"email": "hello@sequant.io"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sequant",
|
|
3
3
|
"description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sequant-io",
|
|
7
7
|
"email": "hello@sequant.io"
|
package/README.md
CHANGED
|
@@ -16,6 +16,11 @@ AI coding agents write code well, but leave you to run the workflow around it
|
|
|
16
16
|
|
|
17
17
|
See the [CHANGELOG](CHANGELOG.md) for release notes, or the [migration guide](CHANGELOG.md#migration-from-v1x) if upgrading from v1.x.
|
|
18
18
|
|
|
19
|
+
### What's new in 2.6
|
|
20
|
+
|
|
21
|
+
- **Boxed Ink TUI is the default for `sequant run`** — on a TTY, `run` now renders the boxed dashboard by default (matching `sequant ready`). Opt out with `--no-tui` (line renderer) or `-s`/`--quiet` (heartbeat-only); non-TTY output auto-degrades.
|
|
22
|
+
- **Flag change:** `--quiet` moved from `-q` to **`-s`** (silent). `-q` is now an alias for `-Q, --quality-loop`, so `sequant run … -q` enables the quality loop as intended. (`--experimental-tui` is kept as a hidden no-op alias.)
|
|
23
|
+
|
|
19
24
|
### What's new in 2.5
|
|
20
25
|
|
|
21
26
|
- **`sequant ready <issue>`** — a post-resolve A+ QA gate that drives a resolved issue through a full-weight `qa → loop → qa` pass and **stops at the human merge gate — it never merges**.
|
package/dist/bin/cli.js
CHANGED
|
@@ -44,7 +44,7 @@ import { logsCommand } from "../src/commands/logs.js";
|
|
|
44
44
|
import { statsCommand } from "../src/commands/stats.js";
|
|
45
45
|
import { dashboardCommand } from "../src/commands/dashboard.js";
|
|
46
46
|
import { stateInitCommand, stateRebuildCommand, stateCleanCommand, } from "../src/commands/state.js";
|
|
47
|
-
import { syncCommand, areSkillsOutdated } from "../src/commands/sync.js";
|
|
47
|
+
import { syncCommand, areSkillsOutdated, checkAndWarnSkillsOutdated, } from "../src/commands/sync.js";
|
|
48
48
|
import { mergeCommand } from "../src/commands/merge.js";
|
|
49
49
|
import { readyCommand, } from "../src/commands/ready.js";
|
|
50
50
|
import { conventionsCommand } from "../src/commands/conventions.js";
|
|
@@ -128,6 +128,7 @@ program
|
|
|
128
128
|
.description("Update templates from the Sequant package")
|
|
129
129
|
.option("-d, --dry-run", "Show what would be updated without making changes")
|
|
130
130
|
.option("-f, --force", "Overwrite local modifications")
|
|
131
|
+
.option("-y, --yes", "Apply updates without prompting (for CI/non-interactive shells)")
|
|
131
132
|
.action(updateCommand);
|
|
132
133
|
program
|
|
133
134
|
.command("sync")
|
|
@@ -399,14 +400,36 @@ locksCmd
|
|
|
399
400
|
// Projects that manage skills manually (no marker) are not affected.
|
|
400
401
|
program.hook("preAction", async (thisCommand) => {
|
|
401
402
|
const cmd = thisCommand.name();
|
|
402
|
-
|
|
403
|
+
// `update` is excluded alongside `init`/`sync`: it is itself the command that
|
|
404
|
+
// resolves drift, so the warn-only "run sync/update" pre-flight would be a
|
|
405
|
+
// circular nag right before it does exactly that.
|
|
406
|
+
if (cmd === "init" || cmd === "sync" || cmd === "update")
|
|
403
407
|
return;
|
|
404
408
|
const manifest = await getManifest();
|
|
405
409
|
if (!manifest)
|
|
406
410
|
return;
|
|
407
|
-
|
|
408
|
-
|
|
411
|
+
// `cache: true` opts the per-command pre-flight into the stat-only drift
|
|
412
|
+
// fingerprint cache: the full template scan runs only when something that
|
|
413
|
+
// affects drift changed, keeping latency off the hot path (AC-5).
|
|
414
|
+
const status = await areSkillsOutdated({ cache: true });
|
|
415
|
+
const { outdated, currentVersion, contentDrift } = status;
|
|
416
|
+
// No version marker → the project manages skills manually; stay silent and
|
|
417
|
+
// do nothing (unchanged behavior — see the header comment above the hook).
|
|
418
|
+
if (currentVersion === null)
|
|
419
|
+
return;
|
|
420
|
+
if (outdated) {
|
|
421
|
+
// Version-marker mismatch → stale install: auto-sync (copy) as before.
|
|
409
422
|
await syncCommand({ quiet: true });
|
|
423
|
+
return;
|
|
424
|
+
}
|
|
425
|
+
// Version-current but bundled content drifted in place (#708/#713). AC-3
|
|
426
|
+
// decision: auto-sync (copy) stays gated on version bumps ONLY — we do NOT
|
|
427
|
+
// copy here, because that would clobber in-place customizations (#711).
|
|
428
|
+
// Content-only drift is surfaced as a non-destructive, warn-only signal,
|
|
429
|
+
// leaving the fix to the user (`sequant sync`/`update`). The helper never
|
|
430
|
+
// touches process.exitCode, so the command still exits normally.
|
|
431
|
+
if (contentDrift > 0) {
|
|
432
|
+
await checkAndWarnSkillsOutdated(status);
|
|
410
433
|
}
|
|
411
434
|
});
|
|
412
435
|
// Parse and execute
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sequant",
|
|
3
3
|
"description": "AI coding agent orchestrator for Claude Code — resolve GitHub issues end-to-end with isolated git worktrees and quality gates, through spec → exec → qa phases.",
|
|
4
|
-
"version": "2.6.
|
|
4
|
+
"version": "2.6.2",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "sequant-io",
|
|
7
7
|
"email": "hello@sequant.io"
|
|
@@ -13,6 +13,9 @@ allowed-tools:
|
|
|
13
13
|
- Bash(gh *)
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
<!-- sequant:local-override -->
|
|
17
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/assess/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/assess` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
18
|
+
|
|
16
19
|
# Unified Issue Assessment & Triage
|
|
17
20
|
|
|
18
21
|
You are the "Assessment Agent" for the current repository.
|
|
@@ -27,6 +27,9 @@ allowed-tools:
|
|
|
27
27
|
- Grep
|
|
28
28
|
---
|
|
29
29
|
|
|
30
|
+
<!-- sequant:local-override -->
|
|
31
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/clean/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/clean` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
32
|
+
|
|
30
33
|
# Repository Cleanup Command
|
|
31
34
|
|
|
32
35
|
Comprehensive, safe repository cleanup that archives stale files, removes artifacts, and commits changes.
|
|
@@ -18,6 +18,9 @@ allowed-tools:
|
|
|
18
18
|
- Bash(gh pr diff:*)
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
+
<!-- sequant:local-override -->
|
|
22
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/docs/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/docs` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
23
|
+
|
|
21
24
|
# Documentation Generator
|
|
22
25
|
|
|
23
26
|
You are the Phase 4 "Documentation Agent" for the current repository.
|
|
@@ -43,6 +43,9 @@ allowed-tools:
|
|
|
43
43
|
- TodoWrite
|
|
44
44
|
---
|
|
45
45
|
|
|
46
|
+
<!-- sequant:local-override -->
|
|
47
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/exec/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/exec` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
48
|
+
|
|
46
49
|
# Implementation Command
|
|
47
50
|
|
|
48
51
|
You are the Phase 2 "Implementation Agent" for the current repository.
|
|
@@ -39,6 +39,9 @@ allowed-tools:
|
|
|
39
39
|
- Bash(./scripts/list-worktrees.sh:*)
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
+
<!-- sequant:local-override -->
|
|
43
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/fullsolve/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/fullsolve` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
44
|
+
|
|
42
45
|
# Full Solve Command
|
|
43
46
|
|
|
44
47
|
You are the "Full Solve Agent" for the current repository.
|
|
@@ -16,6 +16,9 @@ allowed-tools:
|
|
|
16
16
|
- AskUserQuestion
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
<!-- sequant:local-override -->
|
|
20
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/improve/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/improve` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
21
|
+
|
|
19
22
|
# Improve Command
|
|
20
23
|
|
|
21
24
|
You are the "Improvement Agent" for the current repository.
|
|
@@ -665,4 +668,4 @@ Error: Path `src/nonexistent/` not found.
|
|
|
665
668
|
- [ ] **Recommendations** - Tips for running quick wins vs larger refactors
|
|
666
669
|
|
|
667
670
|
**DO NOT proceed to issue creation without user selection.**
|
|
668
|
-
**DO NOT respond until all items are verified.**
|
|
671
|
+
**DO NOT respond until all items are verified.**
|
|
@@ -25,6 +25,9 @@ allowed-tools:
|
|
|
25
25
|
- Bash(git status:*)
|
|
26
26
|
---
|
|
27
27
|
|
|
28
|
+
<!-- sequant:local-override -->
|
|
29
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/loop/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/loop` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
30
|
+
|
|
28
31
|
# Quality Loop Command
|
|
29
32
|
|
|
30
33
|
You are the "Quality Loop Agent" for the current repository.
|
|
@@ -16,6 +16,9 @@ allowed-tools:
|
|
|
16
16
|
- Glob
|
|
17
17
|
---
|
|
18
18
|
|
|
19
|
+
<!-- sequant:local-override -->
|
|
20
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/merger/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/merger` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
21
|
+
|
|
19
22
|
# Merger Skill
|
|
20
23
|
|
|
21
24
|
You are the "Merger Agent" for handling post-QA integration of completed worktrees.
|
|
@@ -24,6 +24,9 @@ allowed-tools:
|
|
|
24
24
|
- AgentOutputTool
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
+
<!-- sequant:local-override -->
|
|
28
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/qa/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/qa` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
29
|
+
|
|
27
30
|
# QA & Code Review
|
|
28
31
|
|
|
29
32
|
You are the Phase 3 "QA & Code Review Agent" for the current repository.
|
|
@@ -12,6 +12,9 @@ allowed-tools:
|
|
|
12
12
|
- Grep
|
|
13
13
|
---
|
|
14
14
|
|
|
15
|
+
<!-- sequant:local-override -->
|
|
16
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/reflect/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/reflect` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
17
|
+
|
|
15
18
|
# Reflection Agent
|
|
16
19
|
|
|
17
20
|
You are the "Reflection Agent" for the current repository.
|
|
@@ -17,6 +17,9 @@ allowed-tools:
|
|
|
17
17
|
- Bash(gh issue comment:*)
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
<!-- sequant:local-override -->
|
|
21
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/security-review/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/security-review` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
22
|
+
|
|
20
23
|
# Security Review Command
|
|
21
24
|
|
|
22
25
|
You are the Security Review Agent for the current repository.
|
|
@@ -30,6 +30,9 @@ allowed-tools:
|
|
|
30
30
|
- Bash(curl:*)
|
|
31
31
|
---
|
|
32
32
|
|
|
33
|
+
<!-- sequant:local-override -->
|
|
34
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/setup/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/setup` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
35
|
+
|
|
33
36
|
# Sequant Setup
|
|
34
37
|
|
|
35
38
|
Initialize Sequant workflow system in your current project.
|
|
@@ -13,6 +13,9 @@ allowed-tools:
|
|
|
13
13
|
- Bash(gh *)
|
|
14
14
|
---
|
|
15
15
|
|
|
16
|
+
<!-- sequant:local-override -->
|
|
17
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/solve/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/solve` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
18
|
+
|
|
16
19
|
# /solve — Deprecated Alias for /assess
|
|
17
20
|
|
|
18
21
|
**This command has been merged into `/assess`.** Use `/assess` instead.
|
|
@@ -17,6 +17,9 @@ allowed-tools:
|
|
|
17
17
|
- AgentOutputTool
|
|
18
18
|
---
|
|
19
19
|
|
|
20
|
+
<!-- sequant:local-override -->
|
|
21
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/spec/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/spec` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
22
|
+
|
|
20
23
|
# Planning Agent
|
|
21
24
|
|
|
22
25
|
Phase 1 "Planning Agent." Understands the issue and AC, reviews or synthesizes a plan, identifies gaps and risks, and drafts a GitHub issue comment.
|
|
@@ -19,6 +19,9 @@ allowed-tools:
|
|
|
19
19
|
- Bash(npx tsx:*)
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
+
<!-- sequant:local-override -->
|
|
23
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/test/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/test` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
24
|
+
|
|
22
25
|
# Browser Testing Command
|
|
23
26
|
|
|
24
27
|
You are the "Testing Agent" for the current repository.
|
|
@@ -20,6 +20,9 @@ allowed-tools:
|
|
|
20
20
|
- Agent(sequant-testgen)
|
|
21
21
|
---
|
|
22
22
|
|
|
23
|
+
<!-- sequant:local-override -->
|
|
24
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/testgen/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/testgen` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
25
|
+
|
|
23
26
|
# Test Generation Command
|
|
24
27
|
|
|
25
28
|
You are the "Test Generation Agent" for the current repository.
|
|
@@ -15,6 +15,9 @@ allowed-tools:
|
|
|
15
15
|
- Bash(gh issue comment:*)
|
|
16
16
|
---
|
|
17
17
|
|
|
18
|
+
<!-- sequant:local-override -->
|
|
19
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/verify/overrides.md` exists. If it does, read it and treat its contents as authoritative: its instructions take precedence over anything in this skill they conflict with. This is the supported way to tailor `/verify` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
20
|
+
|
|
18
21
|
# Execution Verification
|
|
19
22
|
|
|
20
23
|
You are the "Execution Verification Agent" for the current repository.
|
|
@@ -130,7 +130,7 @@ export async function readyCommand(issueArg, options) {
|
|
|
130
130
|
const branch = listWorktrees().find((w) => w.issue === issueNumber)?.branch ?? "";
|
|
131
131
|
adapter = new ReadySnapshotAdapter({ issueNumber, title, branch });
|
|
132
132
|
onProgress = adapter.onProgress;
|
|
133
|
-
const { renderTui } = await import("../ui/tui/
|
|
133
|
+
const { renderTui } = await (await import("../ui/tui/load.js")).loadTui();
|
|
134
134
|
tuiHandle = renderTui(adapter);
|
|
135
135
|
}
|
|
136
136
|
else if (!options.json) {
|
package/dist/src/commands/run.js
CHANGED
|
@@ -92,7 +92,7 @@ export async function runCommand(issues, options) {
|
|
|
92
92
|
maxLoopIterations: resolved.config.maxIterations,
|
|
93
93
|
});
|
|
94
94
|
if (tuiEnabled) {
|
|
95
|
-
const { renderTui } = await import("../ui/tui/
|
|
95
|
+
const { renderTui } = await (await import("../ui/tui/load.js")).loadTui();
|
|
96
96
|
let tuiHandle = null;
|
|
97
97
|
// Unmount the TUI before ShutdownManager writes its shutdown banner so
|
|
98
98
|
// the two don't race on stdout / leave the terminal in alt-screen buffer.
|
|
@@ -13,16 +13,54 @@ interface SyncOptions {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function getSkillsVersion(): Promise<string | null>;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
16
|
+
* Skills status relative to the bundled package, as seen by the pre-flight path.
|
|
17
17
|
*/
|
|
18
|
-
export
|
|
18
|
+
export interface SkillsOutdatedStatus {
|
|
19
|
+
/** Version-marker mismatch (`.sequant-version` ≠ package). Cheap fast-path. */
|
|
19
20
|
outdated: boolean;
|
|
20
21
|
currentVersion: string | null;
|
|
21
22
|
packageVersion: string;
|
|
22
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Count of bundled files that are `new` or `modified` in place at a *matching*
|
|
25
|
+
* version (the #708 blind spot the version marker can't see). Only computed
|
|
26
|
+
* when versions match; `0` otherwise (a mismatch already means stale). Excludes
|
|
27
|
+
* `local-override`/`unchanged` so customized files (e.g. constitution, #711)
|
|
28
|
+
* don't register as drift.
|
|
29
|
+
*/
|
|
30
|
+
contentDrift: number;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Check if skills are outdated compared to package version.
|
|
34
|
+
*
|
|
35
|
+
* The version marker is only a cheap hint: a tree at the matching version can
|
|
36
|
+
* still have drifted bundled content in place (the #708 root cause). So when the
|
|
37
|
+
* marker matches we run the same content diff `sync` uses (`computeTemplateChanges`,
|
|
38
|
+
* the single source of truth from #708/#710) and surface a `contentDrift` count.
|
|
39
|
+
* On a version *mismatch* we skip the diff entirely — the install is already stale
|
|
40
|
+
* and the copy path handles it — keeping the per-command pre-flight cheap (AC-5).
|
|
41
|
+
*
|
|
42
|
+
* `options.cache` opts into a stat-only fingerprint cache for the content scan,
|
|
43
|
+
* so the hot pre-flight path (which runs before most commands, including batched
|
|
44
|
+
* `/assess` dashboard calls) pays the full ~15ms scan only when something that
|
|
45
|
+
* affects drift actually changed. Off by default so diagnostic callers (`doctor`)
|
|
46
|
+
* always see fresh truth.
|
|
47
|
+
*/
|
|
48
|
+
export declare function areSkillsOutdated(options?: {
|
|
49
|
+
cache?: boolean;
|
|
50
|
+
}): Promise<SkillsOutdatedStatus>;
|
|
23
51
|
export declare function syncCommand(options?: SyncOptions): Promise<void>;
|
|
24
52
|
/**
|
|
25
|
-
* Check and warn if skills are outdated (for use by other commands)
|
|
53
|
+
* Check and warn if skills are outdated (for use by other commands).
|
|
54
|
+
*
|
|
55
|
+
* Warns on either signal: a version-marker mismatch, or in-place content drift at
|
|
56
|
+
* a matching version (#708/#713). The content-drift path is warn-only by design —
|
|
57
|
+
* it never mutates files and never sets `process.exitCode` (this is a pre-flight,
|
|
58
|
+
* not the command itself), so customized installs (#711) are left intact.
|
|
59
|
+
*
|
|
60
|
+
* Callers that have already computed the status (e.g. the `preAction` hook) can
|
|
61
|
+
* pass it in to avoid a second template scan on the hot path (AC-5).
|
|
62
|
+
*
|
|
63
|
+
* @returns `true` if a warning was emitted, `false` if up to date.
|
|
26
64
|
*/
|
|
27
|
-
export declare function checkAndWarnSkillsOutdated(): Promise<boolean>;
|
|
65
|
+
export declare function checkAndWarnSkillsOutdated(status?: SkillsOutdatedStatus): Promise<boolean>;
|
|
28
66
|
export {};
|
|
@@ -5,14 +5,23 @@
|
|
|
5
5
|
* Designed for plugin users who need to update after upgrading sequant.
|
|
6
6
|
*/
|
|
7
7
|
import chalk from "chalk";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { createHash } from "crypto";
|
|
8
10
|
import { getManifest, updateManifest, getPackageVersion, } from "../lib/manifest.js";
|
|
9
|
-
import { copyTemplates } from "../lib/templates.js";
|
|
11
|
+
import { copyTemplates, computeTemplateChanges, listTemplateFiles, getTemplatesDir, } from "../lib/templates.js";
|
|
10
12
|
import { getConfig } from "../lib/config.js";
|
|
11
|
-
import { writeFile, readFile, fileExists } from "../lib/fs.js";
|
|
13
|
+
import { writeFile, readFile, fileExists, getFileStats } from "../lib/fs.js";
|
|
12
14
|
import { generateAgentsMd, writeAgentsMd, AGENTS_MD_PATH, } from "../lib/agents-md.js";
|
|
13
15
|
import { getProjectName } from "../lib/project-name.js";
|
|
14
16
|
import { getStackConfig } from "../lib/stacks.js";
|
|
15
17
|
const SKILLS_VERSION_PATH = ".claude/skills/.sequant-version";
|
|
18
|
+
// Where the cheap drift-fingerprint cache lives (gitignored via `**/.sequant/`).
|
|
19
|
+
const DRIFT_CACHE_PATH = ".claude/.sequant/.skills-drift-cache.json";
|
|
20
|
+
// Mirrors config.ts / manifest.ts (those constants are module-private). These
|
|
21
|
+
// install paths are stable; we stat them only to invalidate the drift cache
|
|
22
|
+
// when the project's config tokens or manifest stack change.
|
|
23
|
+
const CONFIG_FILE_PATH = ".claude/.sequant/config.json";
|
|
24
|
+
const MANIFEST_FILE_PATH = ".sequant-manifest.json";
|
|
16
25
|
/**
|
|
17
26
|
* Get the version of skills currently installed
|
|
18
27
|
*/
|
|
@@ -29,16 +38,141 @@ export async function getSkillsVersion() {
|
|
|
29
38
|
}
|
|
30
39
|
}
|
|
31
40
|
/**
|
|
32
|
-
*
|
|
41
|
+
* Cheap stat-only fingerprint of every input that can change the content-drift
|
|
42
|
+
* result: package version plus the mtime (or absence) of each bundled template,
|
|
43
|
+
* its installed counterpart, any `.claude/.local/` override, and the config and
|
|
44
|
+
* manifest. A full read+render+diff scan is ~15ms per command; this fingerprint
|
|
45
|
+
* is ~2-5ms, so the per-command pre-flight can skip the scan when nothing that
|
|
46
|
+
* affects drift has changed (AC-5). A per-file hash (not a max-mtime) is used so
|
|
47
|
+
* editing an *older* file — whose new mtime may still trail another file's —
|
|
48
|
+
* still changes the fingerprint and forces a rescan (no missed warnings).
|
|
49
|
+
*
|
|
50
|
+
* Returns `null` if it cannot be computed; the caller then scans uncached.
|
|
51
|
+
*/
|
|
52
|
+
async function computeDriftFingerprint(packageVersion) {
|
|
53
|
+
try {
|
|
54
|
+
const templateFiles = await listTemplateFiles();
|
|
55
|
+
const templatesDir = getTemplatesDir();
|
|
56
|
+
const lines = [`v=${packageVersion}`];
|
|
57
|
+
const addPath = async (fsPath, key) => {
|
|
58
|
+
try {
|
|
59
|
+
const stats = await getFileStats(fsPath);
|
|
60
|
+
lines.push(`${key}:${Math.round(stats.mtimeMs)}`);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
// Missing file is itself signal: a `.local` override or installed file
|
|
64
|
+
// appearing/disappearing flips this line and invalidates the cache.
|
|
65
|
+
lines.push(`${key}:absent`);
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
for (const templatePath of templateFiles) {
|
|
69
|
+
const normalized = templatePath.replace(/\\/g, "/");
|
|
70
|
+
const localPath = normalized.replace("templates/", ".claude/");
|
|
71
|
+
if (localPath.includes(".local/"))
|
|
72
|
+
continue;
|
|
73
|
+
const templateFsPath = join(templatesDir, normalized.replace("templates/", ""));
|
|
74
|
+
const overridePath = localPath.replace(".claude/", ".claude/.local/");
|
|
75
|
+
await addPath(templateFsPath, `t:${normalized}`);
|
|
76
|
+
await addPath(localPath, `l:${localPath}`);
|
|
77
|
+
await addPath(overridePath, `o:${overridePath}`);
|
|
78
|
+
}
|
|
79
|
+
await addPath(CONFIG_FILE_PATH, "config");
|
|
80
|
+
await addPath(MANIFEST_FILE_PATH, "manifest");
|
|
81
|
+
lines.sort();
|
|
82
|
+
return createHash("sha1").update(lines.join("\n")).digest("hex");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
async function readDriftCache() {
|
|
89
|
+
try {
|
|
90
|
+
if (!(await fileExists(DRIFT_CACHE_PATH)))
|
|
91
|
+
return null;
|
|
92
|
+
const parsed = JSON.parse(await readFile(DRIFT_CACHE_PATH));
|
|
93
|
+
if (typeof parsed?.fingerprint === "string" &&
|
|
94
|
+
typeof parsed?.contentDrift === "number") {
|
|
95
|
+
return parsed;
|
|
96
|
+
}
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
// Corrupt/unreadable cache → treat as a miss; the scan path rebuilds it.
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
async function writeDriftCache(cache) {
|
|
105
|
+
try {
|
|
106
|
+
await writeFile(DRIFT_CACHE_PATH, JSON.stringify(cache));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// The cache is a pure optimization — never fail a command over a write miss
|
|
110
|
+
// (e.g. the `.claude/.sequant/` dir not existing yet).
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Run the content-drift scan (the source-of-truth `computeTemplateChanges` diff),
|
|
115
|
+
* returning the count of `new`+`modified` files. When `useCache` is true (the
|
|
116
|
+
* per-command pre-flight), a stat-only fingerprint short-circuits the scan if no
|
|
117
|
+
* drift-affecting input changed since the last run. Callers that need fresh
|
|
118
|
+
* truth (`doctor`, and `sync` itself) leave caching off — the default.
|
|
33
119
|
*/
|
|
34
|
-
|
|
120
|
+
async function computeContentDrift(packageVersion, useCache) {
|
|
121
|
+
let fingerprint = null;
|
|
122
|
+
if (useCache) {
|
|
123
|
+
fingerprint = await computeDriftFingerprint(packageVersion);
|
|
124
|
+
if (fingerprint) {
|
|
125
|
+
const cached = await readDriftCache();
|
|
126
|
+
if (cached && cached.fingerprint === fingerprint) {
|
|
127
|
+
return cached.contentDrift;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
const manifest = await getManifest();
|
|
133
|
+
if (!manifest)
|
|
134
|
+
return 0;
|
|
135
|
+
const config = await getConfig();
|
|
136
|
+
const tokens = config?.tokens || {};
|
|
137
|
+
const changes = await computeTemplateChanges(manifest.stack, tokens);
|
|
138
|
+
const contentDrift = changes.filter((c) => c.status === "new" || c.status === "modified").length;
|
|
139
|
+
if (useCache && fingerprint) {
|
|
140
|
+
await writeDriftCache({ fingerprint, contentDrift });
|
|
141
|
+
}
|
|
142
|
+
return contentDrift;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
// The pre-flight must never break the actual command. If the content diff
|
|
146
|
+
// fails (missing templates, read error), treat it as "no detectable drift"
|
|
147
|
+
// and let the command proceed.
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if skills are outdated compared to package version.
|
|
153
|
+
*
|
|
154
|
+
* The version marker is only a cheap hint: a tree at the matching version can
|
|
155
|
+
* still have drifted bundled content in place (the #708 root cause). So when the
|
|
156
|
+
* marker matches we run the same content diff `sync` uses (`computeTemplateChanges`,
|
|
157
|
+
* the single source of truth from #708/#710) and surface a `contentDrift` count.
|
|
158
|
+
* On a version *mismatch* we skip the diff entirely — the install is already stale
|
|
159
|
+
* and the copy path handles it — keeping the per-command pre-flight cheap (AC-5).
|
|
160
|
+
*
|
|
161
|
+
* `options.cache` opts into a stat-only fingerprint cache for the content scan,
|
|
162
|
+
* so the hot pre-flight path (which runs before most commands, including batched
|
|
163
|
+
* `/assess` dashboard calls) pays the full ~15ms scan only when something that
|
|
164
|
+
* affects drift actually changed. Off by default so diagnostic callers (`doctor`)
|
|
165
|
+
* always see fresh truth.
|
|
166
|
+
*/
|
|
167
|
+
export async function areSkillsOutdated(options = {}) {
|
|
35
168
|
const currentVersion = await getSkillsVersion();
|
|
36
169
|
const packageVersion = getPackageVersion();
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
packageVersion,
|
|
41
|
-
}
|
|
170
|
+
const outdated = currentVersion !== packageVersion;
|
|
171
|
+
let contentDrift = 0;
|
|
172
|
+
if (!outdated) {
|
|
173
|
+
contentDrift = await computeContentDrift(packageVersion, options.cache === true);
|
|
174
|
+
}
|
|
175
|
+
return { outdated, currentVersion, packageVersion, contentDrift };
|
|
42
176
|
}
|
|
43
177
|
/**
|
|
44
178
|
* Update the skills version marker
|
|
@@ -68,16 +202,35 @@ export async function syncCommand(options = {}) {
|
|
|
68
202
|
console.log(chalk.gray(`Package version: ${packageVersion}`));
|
|
69
203
|
console.log(chalk.gray(`Stack: ${manifest.stack}\n`));
|
|
70
204
|
}
|
|
71
|
-
//
|
|
205
|
+
// Get config tokens for template processing
|
|
206
|
+
const config = await getConfig();
|
|
207
|
+
const tokens = config?.tokens || {};
|
|
208
|
+
// The version marker is only a fast-path hint — verify actual content before
|
|
209
|
+
// claiming "up to date". On a version match we still diff bundled templates
|
|
210
|
+
// against installed content (rendered with the same variables) so we never
|
|
211
|
+
// declare success while real drift sits in place (#708).
|
|
72
212
|
if (!force && skillsVersion === packageVersion) {
|
|
213
|
+
const changes = await computeTemplateChanges(manifest.stack, tokens);
|
|
214
|
+
const drifted = changes.filter((c) => c.status === "new" || c.status === "modified");
|
|
215
|
+
if (drifted.length === 0) {
|
|
216
|
+
// Truthful no-op: content is actually identical.
|
|
217
|
+
if (!quiet) {
|
|
218
|
+
console.log(chalk.green("✔ Skills are already up to date!"));
|
|
219
|
+
}
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
// Version current but content differs — report, don't mutate (report-only
|
|
223
|
+
// keeps the fast path from silently overwriting in-place customizations).
|
|
73
224
|
if (!quiet) {
|
|
74
|
-
console.log(chalk.
|
|
225
|
+
console.log(chalk.yellow(`! Version current, but ${drifted.length} file(s) differ — run \`update\` or \`sync --force\``));
|
|
75
226
|
}
|
|
227
|
+
// Signal drift with a non-zero exit code even under --quiet. The exit code
|
|
228
|
+
// is the machine signal the (suppressible) message is not, so the
|
|
229
|
+
// non-interactive / CI path we recommend can't treat a drifted tree as
|
|
230
|
+
// success — the original failure mode in #708.
|
|
231
|
+
process.exitCode = 1;
|
|
76
232
|
return;
|
|
77
233
|
}
|
|
78
|
-
// Get config tokens for template processing
|
|
79
|
-
const config = await getConfig();
|
|
80
|
-
const tokens = config?.tokens || {};
|
|
81
234
|
// Copy templates with force to overwrite existing files
|
|
82
235
|
const copyOptions = {
|
|
83
236
|
force: true, // Always overwrite when syncing
|
|
@@ -118,14 +271,32 @@ export async function syncCommand(options = {}) {
|
|
|
118
271
|
}
|
|
119
272
|
}
|
|
120
273
|
/**
|
|
121
|
-
* Check and warn if skills are outdated (for use by other commands)
|
|
274
|
+
* Check and warn if skills are outdated (for use by other commands).
|
|
275
|
+
*
|
|
276
|
+
* Warns on either signal: a version-marker mismatch, or in-place content drift at
|
|
277
|
+
* a matching version (#708/#713). The content-drift path is warn-only by design —
|
|
278
|
+
* it never mutates files and never sets `process.exitCode` (this is a pre-flight,
|
|
279
|
+
* not the command itself), so customized installs (#711) are left intact.
|
|
280
|
+
*
|
|
281
|
+
* Callers that have already computed the status (e.g. the `preAction` hook) can
|
|
282
|
+
* pass it in to avoid a second template scan on the hot path (AC-5).
|
|
283
|
+
*
|
|
284
|
+
* @returns `true` if a warning was emitted, `false` if up to date.
|
|
122
285
|
*/
|
|
123
|
-
export async function checkAndWarnSkillsOutdated() {
|
|
124
|
-
const { outdated, currentVersion, packageVersion } = await areSkillsOutdated();
|
|
286
|
+
export async function checkAndWarnSkillsOutdated(status) {
|
|
287
|
+
const { outdated, currentVersion, packageVersion, contentDrift } = status ?? (await areSkillsOutdated());
|
|
125
288
|
if (outdated) {
|
|
126
289
|
console.log(chalk.yellow(`\n! Skills are outdated (${currentVersion || "unknown"} → ${packageVersion})`));
|
|
127
290
|
console.log(chalk.yellow(" Run: npx sequant sync\n"));
|
|
128
291
|
return true;
|
|
129
292
|
}
|
|
293
|
+
if (contentDrift > 0) {
|
|
294
|
+
// Mirror syncCommand's own drift remediation: a bare `sync` at a matching
|
|
295
|
+
// version is report-only (it won't copy), so point at the commands that
|
|
296
|
+
// actually resolve in-place drift — `sync --force` or `update`.
|
|
297
|
+
console.log(chalk.yellow(`\n! Version current, but ${contentDrift} file(s) differ from bundled content`));
|
|
298
|
+
console.log(chalk.yellow(" Run: npx sequant sync --force (or npx sequant update)\n"));
|
|
299
|
+
return true;
|
|
300
|
+
}
|
|
130
301
|
return false;
|
|
131
302
|
}
|