sequant 2.7.0 → 2.9.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 +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +18 -2
- package/dist/bin/cli.d.ts +1 -1
- package/dist/bin/cli.js +12 -2
- package/dist/bin/preflight.d.ts +21 -0
- package/dist/bin/preflight.js +45 -0
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +1 -1
- package/dist/marketplace/external_plugins/sequant/README.md +2 -0
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +18 -3
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +330 -57
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/force-push.md +34 -0
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +117 -19
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +9 -6
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +29 -0
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +100 -2
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +24 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/anti-pattern-detection.md +285 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/call-site-review.md +202 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/quality-gates.md +287 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/test-quality-checklist.md +272 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/references/testing-requirements.md +40 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +95 -11
- package/dist/marketplace/external_plugins/sequant/skills/references/shared/framework-gotchas.md +186 -0
- package/dist/marketplace/external_plugins/sequant/skills/reflect/SKILL.md +27 -13
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/documentation-tiers.md +80 -68
- package/dist/marketplace/external_plugins/sequant/skills/reflect/references/phase-reflection.md +31 -15
- package/dist/marketplace/external_plugins/sequant/skills/release/SKILL.md +669 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/verification-criteria.md +1 -1
- package/dist/marketplace/external_plugins/sequant/skills/test/references/browser-testing-patterns.md +423 -0
- package/dist/marketplace/external_plugins/sequant/skills/upstream/SKILL.md +419 -0
- package/dist/src/commands/logs.js +6 -1
- package/dist/src/commands/run-display.d.ts +20 -0
- package/dist/src/commands/run-display.js +80 -1
- package/dist/src/commands/stats.js +47 -0
- package/dist/src/lib/assess-collision-detect.d.ts +19 -2
- package/dist/src/lib/assess-collision-detect.js +68 -4
- package/dist/src/lib/cli-ui/run-renderer.js +17 -9
- package/dist/src/lib/errors.d.ts +91 -0
- package/dist/src/lib/errors.js +118 -0
- package/dist/src/lib/manifest.js +1 -17
- package/dist/src/lib/version-check.d.ts +19 -0
- package/dist/src/lib/version-check.js +45 -5
- package/dist/src/lib/workflow/batch-executor.d.ts +13 -0
- package/dist/src/lib/workflow/batch-executor.js +142 -24
- package/dist/src/lib/workflow/chain-preflight.d.ts +89 -0
- package/dist/src/lib/workflow/chain-preflight.js +199 -0
- package/dist/src/lib/workflow/chain-resume.d.ts +116 -0
- package/dist/src/lib/workflow/chain-resume.js +166 -0
- package/dist/src/lib/workflow/dependency-markers.d.ts +29 -0
- package/dist/src/lib/workflow/dependency-markers.js +79 -0
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +17 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +29 -0
- package/dist/src/lib/workflow/drivers/claude-code.js +136 -8
- package/dist/src/lib/workflow/error-classifier.d.ts +9 -2
- package/dist/src/lib/workflow/error-classifier.js +14 -1
- package/dist/src/lib/workflow/log-writer.d.ts +1 -1
- package/dist/src/lib/workflow/log-writer.js +6 -8
- package/dist/src/lib/workflow/metrics-schema.d.ts +39 -0
- package/dist/src/lib/workflow/metrics-schema.js +16 -0
- package/dist/src/lib/workflow/metrics-writer.d.ts +2 -1
- package/dist/src/lib/workflow/phase-executor.d.ts +50 -0
- package/dist/src/lib/workflow/phase-executor.js +151 -17
- package/dist/src/lib/workflow/run-log-schema.d.ts +26 -0
- package/dist/src/lib/workflow/run-log-schema.js +52 -1
- package/dist/src/lib/workflow/run-orchestrator.d.ts +14 -0
- package/dist/src/lib/workflow/run-orchestrator.js +291 -30
- package/dist/src/lib/workflow/state-manager.d.ts +1 -0
- package/dist/src/lib/workflow/state-manager.js +6 -0
- package/dist/src/lib/workflow/state-schema.d.ts +3 -0
- package/dist/src/lib/workflow/state-schema.js +7 -0
- package/dist/src/lib/workflow/status-derivation.d.ts +30 -0
- package/dist/src/lib/workflow/status-derivation.js +27 -0
- package/dist/src/lib/workflow/types.d.ts +40 -0
- package/dist/src/lib/workflow/worktree-manager.d.ts +43 -1
- package/dist/src/lib/workflow/worktree-manager.js +103 -33
- package/dist/src/mcp/tools/run.d.ts +2 -0
- package/dist/src/mcp/tools/run.js +2 -0
- package/dist/src/ui/tui/theme.d.ts +18 -4
- package/dist/src/ui/tui/theme.js +18 -4
- package/package.json +5 -6
- package/templates/hooks/post-tool.sh +18 -3
- package/templates/hooks/pre-tool.sh +330 -57
- package/templates/scripts/cleanup-worktree.sh +103 -14
- package/templates/skills/_shared/references/force-push.md +34 -0
- package/templates/skills/assess/SKILL.md +117 -19
- package/templates/skills/assess/references/predicted-collision-detection.md +9 -6
- package/templates/skills/exec/SKILL.md +29 -0
- package/templates/skills/fullsolve/SKILL.md +1 -1
- package/templates/skills/loop/SKILL.md +100 -2
- package/templates/skills/qa/SKILL.md +24 -0
- package/templates/skills/qa/references/anti-pattern-detection.md +285 -0
- package/templates/skills/qa/references/call-site-review.md +202 -0
- package/templates/skills/qa/references/quality-gates.md +287 -0
- package/templates/skills/qa/references/test-quality-checklist.md +272 -0
- package/templates/skills/qa/references/testing-requirements.md +40 -0
- package/templates/skills/qa/scripts/quality-checks.sh +95 -11
- package/templates/skills/references/shared/framework-gotchas.md +186 -0
- package/templates/skills/reflect/SKILL.md +27 -13
- package/templates/skills/reflect/references/documentation-tiers.md +80 -68
- package/templates/skills/reflect/references/phase-reflection.md +31 -15
- package/templates/skills/release/SKILL.md +669 -0
- package/templates/skills/spec/references/verification-criteria.md +1 -1
- package/templates/skills/test/references/browser-testing-patterns.md +423 -0
- package/templates/skills/upstream/SKILL.md +419 -0
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: upstream
|
|
3
|
+
description: "Monitor Claude Code releases, assess compatibility with sequant, and create issues for breaking changes and deprecations. Opportunities are noted in assessment reports for human triage."
|
|
4
|
+
license: MIT
|
|
5
|
+
metadata:
|
|
6
|
+
author: sequant
|
|
7
|
+
version: "1.0"
|
|
8
|
+
allowed-tools:
|
|
9
|
+
- Read
|
|
10
|
+
- Write
|
|
11
|
+
- Glob
|
|
12
|
+
- Grep
|
|
13
|
+
- Bash(gh *)
|
|
14
|
+
- Bash(git *)
|
|
15
|
+
- Bash(jq *)
|
|
16
|
+
- Bash(base64 *)
|
|
17
|
+
- Bash(npx tsx *)
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
<!-- sequant:local-override -->
|
|
21
|
+
> **Local overrides (read this first).** Before following any instruction below, check whether `.claude/.local/skills/upstream/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 `/upstream` without forking it — `overrides.md` lives under `.claude/.local/`, which `sequant update` and `sync` never overwrite.
|
|
22
|
+
|
|
23
|
+
# Upstream: Claude Code Release Tracking
|
|
24
|
+
|
|
25
|
+
You are the "Upstream Assessment Agent" for the sequant repository.
|
|
26
|
+
|
|
27
|
+
## Purpose
|
|
28
|
+
|
|
29
|
+
When invoked as `/upstream`, your job is to:
|
|
30
|
+
|
|
31
|
+
1. Fetch Claude Code release information from the public GitHub repo
|
|
32
|
+
2. Analyze changes against sequant's current capabilities baseline
|
|
33
|
+
3. Detect relevant changes using keyword matching and regex patterns
|
|
34
|
+
4. Skip out-of-scope changes (configured in baseline.json `outOfScope`)
|
|
35
|
+
5. Generate a structured compatibility assessment report with Actionable and Informational sections
|
|
36
|
+
6. Auto-create GitHub issues for breaking changes, deprecations, new tools, and hook changes
|
|
37
|
+
7. List opportunities in the assessment report for human triage (no individual issues created)
|
|
38
|
+
|
|
39
|
+
## Invocation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Analyze latest release
|
|
43
|
+
/upstream
|
|
44
|
+
|
|
45
|
+
# Analyze specific version
|
|
46
|
+
/upstream v2.1.29
|
|
47
|
+
|
|
48
|
+
# Analyze all releases since version
|
|
49
|
+
/upstream --since v2.1.25
|
|
50
|
+
|
|
51
|
+
# Dry-run mode (no issues created)
|
|
52
|
+
/upstream --dry-run
|
|
53
|
+
|
|
54
|
+
# Help
|
|
55
|
+
/upstream --help
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Assessment Process
|
|
59
|
+
|
|
60
|
+
### 1. Parse Arguments
|
|
61
|
+
|
|
62
|
+
Parse the command arguments to determine:
|
|
63
|
+
|
|
64
|
+
- **Target version**: Specific version (e.g., `v2.1.29`) or `latest`
|
|
65
|
+
- **Since version**: If `--since` flag provided, assess all versions since that release
|
|
66
|
+
- **Dry-run mode**: If `--dry-run` flag, generate report but skip issue creation
|
|
67
|
+
- **Help**: If `--help` flag, show usage instructions
|
|
68
|
+
|
|
69
|
+
### 2. Fetch Release Data
|
|
70
|
+
|
|
71
|
+
Fetch release information from the public Claude Code repository:
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
# Get latest release
|
|
75
|
+
gh release view --repo anthropics/claude-code --json tagName,name,body,publishedAt
|
|
76
|
+
|
|
77
|
+
# Get specific version
|
|
78
|
+
gh release view v2.1.29 --repo anthropics/claude-code --json tagName,name,body,publishedAt
|
|
79
|
+
|
|
80
|
+
# List releases for --since support
|
|
81
|
+
gh release list --repo anthropics/claude-code --limit 50 --json tagName,publishedAt
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Load Baseline
|
|
85
|
+
|
|
86
|
+
Load the sequant capabilities baseline from `.sequant/upstream/baseline.json`:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"lastAssessedVersion": "v2.1.25",
|
|
91
|
+
"tools": {
|
|
92
|
+
"core": ["Task", "Bash", "Read", "Write", "Edit", "Glob", "Grep"],
|
|
93
|
+
"optional": ["WebFetch", "WebSearch", "NotebookEdit"]
|
|
94
|
+
},
|
|
95
|
+
"hooks": {
|
|
96
|
+
"used": ["PreToolUse"],
|
|
97
|
+
"files": ["src/hooks/pre-tool-hook.ts"]
|
|
98
|
+
},
|
|
99
|
+
"mcpServers": {
|
|
100
|
+
"required": [],
|
|
101
|
+
"optional": ["chrome-devtools", "context7", "sequential-thinking"]
|
|
102
|
+
},
|
|
103
|
+
"keywords": [
|
|
104
|
+
"Task", "Bash", "hook", "PreToolUse", "PostToolUse",
|
|
105
|
+
"MCP", "permission", "allow", "deny", "tool",
|
|
106
|
+
"background", "parallel", "agent", "subagent",
|
|
107
|
+
"settings", "config", "plugin"
|
|
108
|
+
],
|
|
109
|
+
"dependencyMap": {
|
|
110
|
+
"permission": ["src/hooks/pre-tool-hook.ts", ".claude/settings.json"],
|
|
111
|
+
"hook": ["src/hooks/pre-tool-hook.ts"],
|
|
112
|
+
"Task": [".claude/skills/**/*.md", "src/lib/workflow/*.ts"],
|
|
113
|
+
"MCP": ["docs/mcp-integrations.md", ".claude/settings.json"]
|
|
114
|
+
},
|
|
115
|
+
"outOfScope": [
|
|
116
|
+
"PDF/document processing - users work with code and GitHub issues",
|
|
117
|
+
"Slack/OAuth integrations - workflow is GitHub-centric",
|
|
118
|
+
"Notebook editing - not a data science tool",
|
|
119
|
+
"IDE-specific features (VSCode, JetBrains) - sequant is CLI/terminal focused",
|
|
120
|
+
"Windows-specific fixes - sequant targets macOS/Linux"
|
|
121
|
+
]
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
### 4. Analyze Changes
|
|
126
|
+
|
|
127
|
+
For each release, analyze the changelog/release body:
|
|
128
|
+
|
|
129
|
+
**Step 1: Extract Changes**
|
|
130
|
+
|
|
131
|
+
Parse the release body to extract individual change items. Common formats:
|
|
132
|
+
- Bullet points: `- Added new feature X`
|
|
133
|
+
- "What's changed" sections
|
|
134
|
+
- BREAKING CHANGE markers
|
|
135
|
+
|
|
136
|
+
**Step 2: Relevance Detection**
|
|
137
|
+
|
|
138
|
+
For each change, check if it's relevant to sequant:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
// Keyword matching
|
|
142
|
+
const isRelevant = baseline.keywords.some(kw =>
|
|
143
|
+
change.toLowerCase().includes(kw.toLowerCase())
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Pattern matching
|
|
147
|
+
const patterns = {
|
|
148
|
+
newTool: /added.*tool|new.*tool|introducing/i,
|
|
149
|
+
deprecation: /deprecat|remov|no longer support/i,
|
|
150
|
+
breaking: /breaking|incompatible|must update/i,
|
|
151
|
+
hook: /hook|PreToolUse|PostToolUse/i,
|
|
152
|
+
permission: /permission|allow|deny|ask/i,
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
**Step 3: Categorize**
|
|
157
|
+
|
|
158
|
+
Categorize each relevant change:
|
|
159
|
+
|
|
160
|
+
| Category | Detection Pattern | Issue Labels |
|
|
161
|
+
|----------|------------------|--------------|
|
|
162
|
+
| `breaking` | Breaking, incompatible, must update | `upstream`, `bug`, `priority:high` |
|
|
163
|
+
| `deprecation` | Deprecated, removed, no longer supported | `upstream`, `bug` |
|
|
164
|
+
| `new-tool` | Added tool, new tool, introducing | `upstream`, `enhancement` |
|
|
165
|
+
| `hook-change` | Hook, PreToolUse, PostToolUse | `upstream`, `enhancement` |
|
|
166
|
+
| `opportunity` | Keywords match but not above categories | (no issue — noted in assessment for human triage) |
|
|
167
|
+
| `no-action` | Doesn't match patterns or keywords | (no issue) |
|
|
168
|
+
|
|
169
|
+
**Step 4: Impact Mapping**
|
|
170
|
+
|
|
171
|
+
For relevant changes, map to affected sequant files using `dependencyMap`:
|
|
172
|
+
|
|
173
|
+
```typescript
|
|
174
|
+
const impactFiles = baseline.dependencyMap[matchedKeyword] || [];
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
### 5. Check for Duplicates
|
|
178
|
+
|
|
179
|
+
Before creating issues, check for existing upstream issues:
|
|
180
|
+
|
|
181
|
+
```bash
|
|
182
|
+
# Search for similar issues
|
|
183
|
+
gh issue list --label upstream --search "<finding-title>" --json number,title
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
If a similar issue exists:
|
|
187
|
+
- Add a comment linking to the new assessment
|
|
188
|
+
- Skip creating a duplicate
|
|
189
|
+
|
|
190
|
+
### 6. Generate Outputs
|
|
191
|
+
|
|
192
|
+
**Output 1: Assessment Report (GitHub Issue)**
|
|
193
|
+
|
|
194
|
+
Create a summary issue with the full assessment:
|
|
195
|
+
|
|
196
|
+
```markdown
|
|
197
|
+
## Upstream: Claude Code <version> Assessment
|
|
198
|
+
|
|
199
|
+
**Release:** [<version>](https://github.com/anthropics/claude-code/releases/tag/<version>)
|
|
200
|
+
**Released:** <date>
|
|
201
|
+
**Assessed:** <today>
|
|
202
|
+
|
|
203
|
+
### Summary
|
|
204
|
+
|
|
205
|
+
| Category | Count | Action Required |
|
|
206
|
+
|----------|-------|-----------------|
|
|
207
|
+
| Breaking Changes | N | [status] |
|
|
208
|
+
| New Tools | N | [status] |
|
|
209
|
+
| Deprecations | N | [status] |
|
|
210
|
+
| Opportunities | N | [status] |
|
|
211
|
+
|
|
212
|
+
### Actionable
|
|
213
|
+
|
|
214
|
+
*Breaking changes, deprecations, and other items that affect sequant.*
|
|
215
|
+
|
|
216
|
+
[list of breaking, deprecation, new-tool, hook-change findings]
|
|
217
|
+
|
|
218
|
+
### Informational
|
|
219
|
+
|
|
220
|
+
*Opportunities noted for human triage. No individual issues auto-created.*
|
|
221
|
+
|
|
222
|
+
[list of opportunity findings]
|
|
223
|
+
|
|
224
|
+
### No Action Required
|
|
225
|
+
|
|
226
|
+
[list of irrelevant changes]
|
|
227
|
+
|
|
228
|
+
---
|
|
229
|
+
|
|
230
|
+
*Generated by /upstream skill*
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
**Output 2: Individual Issues (Actionable Findings)**
|
|
234
|
+
|
|
235
|
+
For each actionable finding (breaking, deprecation, new-tool, hook-change), create an issue.
|
|
236
|
+
**Note:** Opportunities do NOT get individual issues — they are listed in the assessment report's Informational section for human triage.
|
|
237
|
+
|
|
238
|
+
```markdown
|
|
239
|
+
## feat: Leverage <feature> from Claude Code <version>
|
|
240
|
+
|
|
241
|
+
**Upstream:** Claude Code <version>
|
|
242
|
+
**Category:** <category>
|
|
243
|
+
**Assessment:** #<assessment-issue>
|
|
244
|
+
|
|
245
|
+
### Context
|
|
246
|
+
|
|
247
|
+
<description from release notes>
|
|
248
|
+
|
|
249
|
+
### Opportunity
|
|
250
|
+
|
|
251
|
+
<how sequant could use this>
|
|
252
|
+
|
|
253
|
+
### Proposed Implementation
|
|
254
|
+
|
|
255
|
+
[To be determined during /spec phase]
|
|
256
|
+
|
|
257
|
+
### Acceptance Criteria
|
|
258
|
+
|
|
259
|
+
- [ ] AC-1: [To be defined]
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
263
|
+
*Auto-created by /upstream assessment #<N>*
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Labels: `upstream`, `needs-triage`, `enhancement`
|
|
267
|
+
|
|
268
|
+
**Output 3: Local Report**
|
|
269
|
+
|
|
270
|
+
Save to `.sequant/upstream/<version>.md`:
|
|
271
|
+
|
|
272
|
+
```markdown
|
|
273
|
+
# Claude Code <version> Assessment
|
|
274
|
+
|
|
275
|
+
Assessed: <date>
|
|
276
|
+
Previous: <last-assessed-version>
|
|
277
|
+
|
|
278
|
+
## Summary
|
|
279
|
+
|
|
280
|
+
[same as GitHub issue]
|
|
281
|
+
|
|
282
|
+
## Raw Findings
|
|
283
|
+
|
|
284
|
+
[detailed analysis data]
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
**Output 4: Update Baseline**
|
|
288
|
+
|
|
289
|
+
Update `.sequant/upstream/baseline.json`:
|
|
290
|
+
- Set `lastAssessedVersion` to the assessed version
|
|
291
|
+
|
|
292
|
+
### 7. Multi-Version Batching
|
|
293
|
+
|
|
294
|
+
When `--since <version>` is used:
|
|
295
|
+
|
|
296
|
+
1. List all releases after the specified version
|
|
297
|
+
2. Assess each version individually
|
|
298
|
+
3. Create a batched summary issue linking all individual assessments
|
|
299
|
+
4. Update baseline to latest assessed version
|
|
300
|
+
|
|
301
|
+
## Dry-Run Mode
|
|
302
|
+
|
|
303
|
+
When `--dry-run` is specified:
|
|
304
|
+
|
|
305
|
+
1. Perform full analysis
|
|
306
|
+
2. Generate local report
|
|
307
|
+
3. Output what issues WOULD be created (titles, labels)
|
|
308
|
+
4. Skip actual GitHub issue creation
|
|
309
|
+
5. Skip baseline update
|
|
310
|
+
|
|
311
|
+
## Error Handling
|
|
312
|
+
|
|
313
|
+
- **No releases found**: Exit with clear message
|
|
314
|
+
- **Baseline missing**: Create default baseline, warn user
|
|
315
|
+
- **GitHub API errors**: Retry with backoff, then fail gracefully
|
|
316
|
+
- **Already assessed**: Skip with message (idempotent)
|
|
317
|
+
|
|
318
|
+
## Output Verification
|
|
319
|
+
|
|
320
|
+
**Before completing, verify:**
|
|
321
|
+
|
|
322
|
+
- [ ] Release data successfully fetched
|
|
323
|
+
- [ ] Baseline loaded (or created with defaults)
|
|
324
|
+
- [ ] Each change categorized
|
|
325
|
+
- [ ] Duplicates checked before issue creation
|
|
326
|
+
- [ ] Assessment report created (or dry-run output shown)
|
|
327
|
+
- [ ] Individual issues created for actionable findings (not opportunities)
|
|
328
|
+
- [ ] Local report saved
|
|
329
|
+
- [ ] Baseline updated with new version
|
|
330
|
+
|
|
331
|
+
## Examples
|
|
332
|
+
|
|
333
|
+
### Example 1: Assess Latest Release
|
|
334
|
+
|
|
335
|
+
```
|
|
336
|
+
/upstream
|
|
337
|
+
|
|
338
|
+
Fetching latest Claude Code release...
|
|
339
|
+
Release: v2.1.29 (2025-01-31)
|
|
340
|
+
|
|
341
|
+
Loading baseline from .sequant/upstream/baseline.json...
|
|
342
|
+
Last assessed: v2.1.27
|
|
343
|
+
|
|
344
|
+
Analyzing 12 changes from release notes...
|
|
345
|
+
- 3 relevant changes detected
|
|
346
|
+
- 9 no-action changes
|
|
347
|
+
|
|
348
|
+
Findings:
|
|
349
|
+
1. [opportunity] New --background flag on Task tool
|
|
350
|
+
Matched keywords: Task, background
|
|
351
|
+
Impact files: .claude/skills/**/*.md
|
|
352
|
+
|
|
353
|
+
2. [hook-change] Permissions now respect content-level ask
|
|
354
|
+
Matched keywords: permission
|
|
355
|
+
Impact files: src/hooks/pre-tool-hook.ts
|
|
356
|
+
|
|
357
|
+
3. [deprecation] oldHookName deprecated
|
|
358
|
+
Matched pattern: deprecat
|
|
359
|
+
Impact files: src/hooks/pre-tool-hook.ts
|
|
360
|
+
|
|
361
|
+
Creating assessment issue...
|
|
362
|
+
Created: #250 - Upstream: Claude Code v2.1.29 Assessment
|
|
363
|
+
|
|
364
|
+
Creating individual issues...
|
|
365
|
+
Created: #251 - feat: Leverage new --background flag from Claude Code v2.1.29
|
|
366
|
+
Created: #252 - chore: Update to new hook name (deprecation from v2.1.29)
|
|
367
|
+
|
|
368
|
+
Saving local report to .sequant/upstream/v2.1.29.md...
|
|
369
|
+
Updating baseline lastAssessedVersion to v2.1.29...
|
|
370
|
+
|
|
371
|
+
Done! Assessment complete.
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
### Example 2: Dry Run
|
|
375
|
+
|
|
376
|
+
```
|
|
377
|
+
/upstream --dry-run
|
|
378
|
+
|
|
379
|
+
[DRY RUN MODE - No issues will be created]
|
|
380
|
+
|
|
381
|
+
Fetching latest Claude Code release...
|
|
382
|
+
Release: v2.1.29 (2025-01-31)
|
|
383
|
+
|
|
384
|
+
...analysis...
|
|
385
|
+
|
|
386
|
+
Would create issues:
|
|
387
|
+
1. Assessment: Upstream: Claude Code v2.1.29 Assessment
|
|
388
|
+
2. Finding: feat: Leverage new --background flag from Claude Code v2.1.29
|
|
389
|
+
3. Finding: chore: Update to new hook name (deprecation from v2.1.29)
|
|
390
|
+
|
|
391
|
+
Local report saved: .sequant/upstream/v2.1.29.md
|
|
392
|
+
Baseline NOT updated (dry-run mode)
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Example 3: Already Assessed
|
|
396
|
+
|
|
397
|
+
```
|
|
398
|
+
/upstream
|
|
399
|
+
|
|
400
|
+
Fetching latest Claude Code release...
|
|
401
|
+
Release: v2.1.29 (2025-01-31)
|
|
402
|
+
|
|
403
|
+
Already assessed: .sequant/upstream/v2.1.29.md exists
|
|
404
|
+
Use --force to re-assess.
|
|
405
|
+
|
|
406
|
+
No action taken.
|
|
407
|
+
```
|
|
408
|
+
|
|
409
|
+
## Notes
|
|
410
|
+
|
|
411
|
+
- This is an internal tool for sequant maintainers
|
|
412
|
+
- All created issues get `needs-triage` label for human review
|
|
413
|
+
- The skill is read-only for Claude Code repo (no PRs or edits)
|
|
414
|
+
- Baseline file should be committed to version control
|
|
415
|
+
- Local reports in `.sequant/upstream/` should be committed
|
|
416
|
+
|
|
417
|
+
---
|
|
418
|
+
|
|
419
|
+
*This skill monitors the upstream Claude Code project to help sequant stay current with new features and breaking changes.*
|
|
@@ -81,6 +81,9 @@ function formatTime(isoString) {
|
|
|
81
81
|
function displayLogSummary(log, filename, options) {
|
|
82
82
|
const passed = log.summary.passed;
|
|
83
83
|
const failed = log.summary.failed;
|
|
84
|
+
// #766: `partial` gets its own bucket so an all-partial run isn't rendered as
|
|
85
|
+
// `0 passed, 0 failed` (older logs predate the field → default to 0).
|
|
86
|
+
const partial = log.summary.partial ?? 0;
|
|
84
87
|
const total = log.summary.totalIssues;
|
|
85
88
|
const status = failed > 0
|
|
86
89
|
? chalk.red("FAILED")
|
|
@@ -91,7 +94,9 @@ function displayLogSummary(log, filename, options) {
|
|
|
91
94
|
console.log(chalk.gray(` File: ${filename}`));
|
|
92
95
|
console.log(chalk.gray(` Time: ${formatTime(log.startTime)}`));
|
|
93
96
|
console.log(chalk.gray(` Duration: ${formatDuration(log.summary.totalDurationSeconds)}`));
|
|
94
|
-
console.log(chalk.gray(` Status: ${status} (${passed}/${total} passed, ${failed} failed
|
|
97
|
+
console.log(chalk.gray(` Status: ${status} (${passed}/${total} passed, ${failed} failed` +
|
|
98
|
+
(partial > 0 ? `, ${partial} partial` : "") +
|
|
99
|
+
`)`));
|
|
95
100
|
console.log(chalk.gray(` Phases: ${log.config.phases.join(" → ")}`));
|
|
96
101
|
// Show issues
|
|
97
102
|
for (const issue of log.issues) {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { RunRenderer } from "../lib/cli-ui/run-renderer-types.js";
|
|
11
11
|
import type { ResolvedRun, RunResult } from "../lib/workflow/run-orchestrator.js";
|
|
12
|
+
import type { IssueResult } from "../lib/workflow/types.js";
|
|
12
13
|
/**
|
|
13
14
|
* Print pre-run config block.
|
|
14
15
|
*
|
|
@@ -16,6 +17,25 @@ import type { ResolvedRun, RunResult } from "../lib/workflow/run-orchestrator.js
|
|
|
16
17
|
* appear when non-default, matching the pre-#503 format.
|
|
17
18
|
*/
|
|
18
19
|
export declare function displayConfig(r: ResolvedRun): void;
|
|
20
|
+
/**
|
|
21
|
+
* Detect a chain halted by a rate-limit/billing failure and build the summary
|
|
22
|
+
* notice for it (#761 AC-5). Returns null when the run wasn't a chain, no
|
|
23
|
+
* issue failed, or the halting failure wasn't rate-limit-classified.
|
|
24
|
+
*
|
|
25
|
+
* Extracted from `displaySummary` so the halt-and-print decision is testable
|
|
26
|
+
* standalone — the same treatment #760 gave `planChainResumeFromState` when it
|
|
27
|
+
* hit the executeSequential testability wall.
|
|
28
|
+
*
|
|
29
|
+
* The failing phase is found with the same reverse non-loop scan as
|
|
30
|
+
* `toIssueSummary` (#766): the classification must describe the LAST attempt,
|
|
31
|
+
* not a stale first-iteration failure.
|
|
32
|
+
*
|
|
33
|
+
* @internal Exported for testing
|
|
34
|
+
*/
|
|
35
|
+
export declare function buildRateLimitHaltNotice(results: IssueResult[], chainEnabled: boolean): {
|
|
36
|
+
issueNumber: number;
|
|
37
|
+
label: string;
|
|
38
|
+
} | null;
|
|
19
39
|
/**
|
|
20
40
|
* Print post-run summary: per-issue grid, log path, reflection, tips.
|
|
21
41
|
*
|
|
@@ -10,7 +10,9 @@
|
|
|
10
10
|
import chalk from "chalk";
|
|
11
11
|
import { ui, colors } from "../lib/cli-ui.js";
|
|
12
12
|
import { renderRunSummary } from "../lib/cli-ui/run-renderer.js";
|
|
13
|
+
import { BillingError, RateLimitError, formatRateLimitMessage, isBillingFailure, } from "../lib/errors.js";
|
|
13
14
|
import { analyzeRun, formatReflection } from "../lib/workflow/run-reflect.js";
|
|
15
|
+
import { LOOP_PHASE } from "../lib/workflow/status-derivation.js";
|
|
14
16
|
/**
|
|
15
17
|
* Print pre-run config block.
|
|
16
18
|
*
|
|
@@ -67,7 +69,19 @@ export function displayConfig(r) {
|
|
|
67
69
|
* Convert workflow `IssueResult` to renderer `IssueSummary`.
|
|
68
70
|
*/
|
|
69
71
|
function toIssueSummary(r) {
|
|
70
|
-
|
|
72
|
+
// #766: the reason to show is the LAST failing attempt, not the first.
|
|
73
|
+
// `phaseResults` accumulates every attempt across every quality-loop
|
|
74
|
+
// iteration, so `.find()` (first-wins) rendered a stale first-iteration
|
|
75
|
+
// reason: #762's cell read `Timeout after 1800s` when its real last failure
|
|
76
|
+
// was an API drop. `verdict`/`unmetCount` below hang off the same entry, so
|
|
77
|
+
// they were stale for the same reason. `loop` is excluded on the same grounds
|
|
78
|
+
// the card and log exclude it (see `status-derivation.ts`) — it is auxiliary
|
|
79
|
+
// recovery, and a trailing loop failure would mask the phase that actually
|
|
80
|
+
// failed. Reverse scan rather than `findLast`: tsconfig pins `lib: ES2022`
|
|
81
|
+
// and `findLast` is ES2023.
|
|
82
|
+
const failedPhase = [...r.phaseResults]
|
|
83
|
+
.reverse()
|
|
84
|
+
.find((p) => !p.success && p.phase !== LOOP_PHASE);
|
|
71
85
|
const summary = {
|
|
72
86
|
issueNumber: r.issueNumber,
|
|
73
87
|
success: r.success,
|
|
@@ -91,6 +105,48 @@ function toIssueSummary(r) {
|
|
|
91
105
|
}
|
|
92
106
|
return summary;
|
|
93
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Detect a chain halted by a rate-limit/billing failure and build the summary
|
|
110
|
+
* notice for it (#761 AC-5). Returns null when the run wasn't a chain, no
|
|
111
|
+
* issue failed, or the halting failure wasn't rate-limit-classified.
|
|
112
|
+
*
|
|
113
|
+
* Extracted from `displaySummary` so the halt-and-print decision is testable
|
|
114
|
+
* standalone — the same treatment #760 gave `planChainResumeFromState` when it
|
|
115
|
+
* hit the executeSequential testability wall.
|
|
116
|
+
*
|
|
117
|
+
* The failing phase is found with the same reverse non-loop scan as
|
|
118
|
+
* `toIssueSummary` (#766): the classification must describe the LAST attempt,
|
|
119
|
+
* not a stale first-iteration failure.
|
|
120
|
+
*
|
|
121
|
+
* @internal Exported for testing
|
|
122
|
+
*/
|
|
123
|
+
export function buildRateLimitHaltNotice(results, chainEnabled) {
|
|
124
|
+
if (!chainEnabled)
|
|
125
|
+
return null;
|
|
126
|
+
// Chain mode halts at the first failed link, so at most one failed issue
|
|
127
|
+
// exists; scan defensively anyway.
|
|
128
|
+
for (const r of results) {
|
|
129
|
+
if (r.success)
|
|
130
|
+
continue;
|
|
131
|
+
const failedPhase = [...r.phaseResults]
|
|
132
|
+
.reverse()
|
|
133
|
+
.find((p) => !p.success && p.phase !== LOOP_PHASE);
|
|
134
|
+
const err = failedPhase?.structuredError;
|
|
135
|
+
if (err instanceof RateLimitError || err instanceof BillingError) {
|
|
136
|
+
// Event-derived errors get their message from formatRateLimitMessage, so
|
|
137
|
+
// re-deriving from metadata is exact and includes resetsAt. But errors
|
|
138
|
+
// from the assistant-error channel carry no billing/reset metadata —
|
|
139
|
+
// re-deriving there would mislabel a BillingError as "Rate limited"
|
|
140
|
+
// (isBillingFailure({}) is false) — so fall back to the typed message
|
|
141
|
+
// when the metadata carries no signal.
|
|
142
|
+
const label = err.metadata.resetsAt !== undefined || isBillingFailure(err.metadata)
|
|
143
|
+
? formatRateLimitMessage(err.metadata)
|
|
144
|
+
: err.message;
|
|
145
|
+
return { issueNumber: r.issueNumber, label };
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
94
150
|
/**
|
|
95
151
|
* Print post-run summary: per-issue grid, log path, reflection, tips.
|
|
96
152
|
*
|
|
@@ -120,6 +176,29 @@ export function displaySummary(result, renderer) {
|
|
|
120
176
|
dryRun: config.dryRun,
|
|
121
177
|
});
|
|
122
178
|
}
|
|
179
|
+
// #760: a chain link whose checkpoint commit failed keeps its own work but
|
|
180
|
+
// loses the recovery point resume depends on, and the per-issue warning has
|
|
181
|
+
// long scrolled past by now on a multi-hour chain. Restate it at the summary,
|
|
182
|
+
// where the user is actually looking, so the next run's fail-fast is expected.
|
|
183
|
+
const checkpointFailures = results.filter((r) => r.checkpointFailed);
|
|
184
|
+
if (checkpointFailures.length > 0) {
|
|
185
|
+
console.log(colors.warning(` ⚠️ Checkpoint commit failed for ${checkpointFailures
|
|
186
|
+
.map((r) => `#${r.issueNumber}`)
|
|
187
|
+
.join(", ")} — uncommitted work is missing from the feature branch.`));
|
|
188
|
+
console.log(colors.muted(" Resuming this chain will stop at that link until the work is committed (or use --force)."));
|
|
189
|
+
console.log("");
|
|
190
|
+
}
|
|
191
|
+
// #761: a chain halted by a rate limit already stopped at the right link,
|
|
192
|
+
// but the labeled cause has long scrolled past by now (same rationale as the
|
|
193
|
+
// #760 restatement above), and #760 added no resume flag — resume IS
|
|
194
|
+
// re-running the identical command. Say both explicitly, or the halt reads
|
|
195
|
+
// as a bug and the resume path stays undiscovered.
|
|
196
|
+
const rateLimitHalt = buildRateLimitHaltNotice(results, mergedOptions.chain === true);
|
|
197
|
+
if (rateLimitHalt) {
|
|
198
|
+
console.log(colors.warning(` ⚠️ ${rateLimitHalt.label} — chain halted at #${rateLimitHalt.issueNumber}.`));
|
|
199
|
+
console.log(colors.muted(` Re-run the same command to resume from #${rateLimitHalt.issueNumber} (no flag needed; completed links are skipped).`));
|
|
200
|
+
console.log("");
|
|
201
|
+
}
|
|
123
202
|
if (mergedOptions.reflect && results.length > 0) {
|
|
124
203
|
const reflection = analyzeRun({
|
|
125
204
|
results,
|
|
@@ -263,6 +263,35 @@ function loadMetrics() {
|
|
|
263
263
|
return null;
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
+
/**
|
|
267
|
+
* Compute the failure-category breakdown over runs that recorded a failure —
|
|
268
|
+
* outcome "failed" (every issue failed) or "partial" (at least one issue
|
|
269
|
+
* failed) (#783).
|
|
270
|
+
*
|
|
271
|
+
* NOTE — deviation from AC-1's literal "over failed runs" wording: partial runs
|
|
272
|
+
* also carry a failureCategory (recorded whenever >=1 issue fails) and are
|
|
273
|
+
* genuine signal for "what's killing my runs", so they are counted here by
|
|
274
|
+
* explicit user decision. Success runs never carry the field and are excluded.
|
|
275
|
+
*
|
|
276
|
+
* Runs without the field (pre-#761 records, or a partial whose failure was
|
|
277
|
+
* never categorized) are bucketed as "unclassified" — never dropped and never
|
|
278
|
+
* conflated with the "unknown" enum value.
|
|
279
|
+
*
|
|
280
|
+
* Pure function of the passed runs array, so any upstream cohort filtering is
|
|
281
|
+
* automatically respected (computed after filtering, not before).
|
|
282
|
+
*/
|
|
283
|
+
function calculateFailureCategoryBreakdown(runs) {
|
|
284
|
+
const counts = new Map();
|
|
285
|
+
for (const run of runs) {
|
|
286
|
+
if (run.outcome === "success")
|
|
287
|
+
continue;
|
|
288
|
+
const category = run.failureCategory ?? "unclassified";
|
|
289
|
+
counts.set(category, (counts.get(category) ?? 0) + 1);
|
|
290
|
+
}
|
|
291
|
+
return [...counts.entries()]
|
|
292
|
+
.map(([category, count]) => ({ category, count }))
|
|
293
|
+
.sort((a, b) => b.count - a.count || a.category.localeCompare(b.category));
|
|
294
|
+
}
|
|
266
295
|
/**
|
|
267
296
|
* Calculate analytics from metrics
|
|
268
297
|
*/
|
|
@@ -274,6 +303,7 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
274
303
|
successCount: 0,
|
|
275
304
|
partialCount: 0,
|
|
276
305
|
failedCount: 0,
|
|
306
|
+
failureCategories: [],
|
|
277
307
|
successRate: 0,
|
|
278
308
|
avgTokensPerRun: 0,
|
|
279
309
|
avgFilesChanged: 0,
|
|
@@ -294,6 +324,7 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
294
324
|
const successCount = runs.filter((r) => r.outcome === "success").length;
|
|
295
325
|
const partialCount = runs.filter((r) => r.outcome === "partial").length;
|
|
296
326
|
const failedCount = runs.filter((r) => r.outcome === "failed").length;
|
|
327
|
+
const failureCategories = calculateFailureCategoryBreakdown(runs);
|
|
297
328
|
const successRate = (successCount / runs.length) * 100;
|
|
298
329
|
const avgTokensPerRun = runs.reduce((sum, r) => sum + r.metrics.tokensUsed, 0) / runs.length;
|
|
299
330
|
const avgFilesChanged = runs.reduce((sum, r) => sum + r.metrics.filesChanged, 0) / runs.length;
|
|
@@ -323,6 +354,7 @@ function calculateMetricsAnalytics(metrics) {
|
|
|
323
354
|
successCount,
|
|
324
355
|
partialCount,
|
|
325
356
|
failedCount,
|
|
357
|
+
failureCategories,
|
|
326
358
|
successRate,
|
|
327
359
|
avgTokensPerRun,
|
|
328
360
|
avgFilesChanged,
|
|
@@ -415,6 +447,19 @@ function displayMetricsAnalytics(analytics) {
|
|
|
415
447
|
const failedRate = (analytics.failedCount / total) * 100;
|
|
416
448
|
console.log(` ${colors.error("\u2717 Failed")} ${analytics.failedCount} (${failedRate.toFixed(0)}%) ${failedBar}`);
|
|
417
449
|
}
|
|
450
|
+
// Failure-category breakdown (#783) \u2014 adjacent to the outcome bars above.
|
|
451
|
+
// Counts runs that recorded a failure (outcome "failed" or "partial"); hidden
|
|
452
|
+
// entirely when there are none (AC-3).
|
|
453
|
+
if (analytics.failureCategories.length > 0) {
|
|
454
|
+
const failureRunCount = analytics.failureCategories.reduce((sum, b) => sum + b.count, 0);
|
|
455
|
+
const runNoun = failureRunCount === 1 ? "run with a failure" : "runs with a failure";
|
|
456
|
+
console.log(ui.sectionHeader(`Failure Categories (${failureRunCount} ${runNoun})`));
|
|
457
|
+
const pad = Math.max(...analytics.failureCategories.map((b) => b.category.length));
|
|
458
|
+
for (const bucket of analytics.failureCategories) {
|
|
459
|
+
const pct = ((bucket.count / failureRunCount) * 100).toFixed(0);
|
|
460
|
+
console.log(` ${bucket.category.padEnd(pad)} ${bucket.count} (${pct}%)`);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
418
463
|
// Averages table
|
|
419
464
|
console.log(ui.sectionHeader("Averages"));
|
|
420
465
|
const avgData = {};
|
|
@@ -653,6 +698,8 @@ export async function statsCommand(options) {
|
|
|
653
698
|
partialCount: analytics.partialCount,
|
|
654
699
|
failedCount: analytics.failedCount,
|
|
655
700
|
successRate: analytics.successRate,
|
|
701
|
+
// Failure-category breakdown over failed runs (#783 AC-5)
|
|
702
|
+
failureCategories: analytics.failureCategories,
|
|
656
703
|
avgTokensPerRun: analytics.avgTokensPerRun,
|
|
657
704
|
avgFilesChanged: analytics.avgFilesChanged,
|
|
658
705
|
avgLinesAdded: analytics.avgLinesAdded,
|