sequant 1.12.0 → 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -8
- package/dist/bin/cli.js +19 -9
- package/dist/src/commands/doctor.js +42 -20
- package/dist/src/commands/init.js +152 -65
- package/dist/src/commands/logs.js +7 -6
- package/dist/src/commands/run.d.ts +13 -1
- package/dist/src/commands/run.js +122 -32
- package/dist/src/commands/stats.js +67 -48
- package/dist/src/commands/status.js +30 -12
- package/dist/src/commands/sync.d.ts +28 -0
- package/dist/src/commands/sync.js +102 -0
- package/dist/src/index.d.ts +6 -0
- package/dist/src/index.js +4 -0
- package/dist/src/lib/cli-ui.d.ts +196 -0
- package/dist/src/lib/cli-ui.js +544 -0
- package/dist/src/lib/content-analyzer.d.ts +89 -0
- package/dist/src/lib/content-analyzer.js +437 -0
- package/dist/src/lib/phase-signal.d.ts +94 -0
- package/dist/src/lib/phase-signal.js +171 -0
- package/dist/src/lib/phase-spinner.d.ts +146 -0
- package/dist/src/lib/phase-spinner.js +255 -0
- package/dist/src/lib/solve-comment-parser.d.ts +84 -0
- package/dist/src/lib/solve-comment-parser.js +200 -0
- package/dist/src/lib/stack-config.d.ts +51 -0
- package/dist/src/lib/stack-config.js +77 -0
- package/dist/src/lib/stacks.d.ts +52 -0
- package/dist/src/lib/stacks.js +173 -0
- package/dist/src/lib/templates.d.ts +2 -0
- package/dist/src/lib/templates.js +9 -2
- package/dist/src/lib/upstream/assessment.d.ts +70 -0
- package/dist/src/lib/upstream/assessment.js +385 -0
- package/dist/src/lib/upstream/index.d.ts +11 -0
- package/dist/src/lib/upstream/index.js +14 -0
- package/dist/src/lib/upstream/issues.d.ts +38 -0
- package/dist/src/lib/upstream/issues.js +267 -0
- package/dist/src/lib/upstream/relevance.d.ts +50 -0
- package/dist/src/lib/upstream/relevance.js +209 -0
- package/dist/src/lib/upstream/report.d.ts +29 -0
- package/dist/src/lib/upstream/report.js +391 -0
- package/dist/src/lib/upstream/types.d.ts +207 -0
- package/dist/src/lib/upstream/types.js +5 -0
- package/dist/src/lib/workflow/log-writer.d.ts +1 -1
- package/dist/src/lib/workflow/metrics-schema.d.ts +3 -3
- package/dist/src/lib/workflow/qa-cache.d.ts +199 -0
- package/dist/src/lib/workflow/qa-cache.js +440 -0
- package/dist/src/lib/workflow/run-log-schema.d.ts +34 -6
- package/dist/src/lib/workflow/run-log-schema.js +12 -1
- package/dist/src/lib/workflow/state-schema.d.ts +4 -4
- package/dist/src/lib/workflow/types.d.ts +4 -0
- package/package.json +6 -1
- package/templates/skills/qa/scripts/quality-checks.sh +509 -53
- package/templates/skills/solve/SKILL.md +375 -83
- package/templates/skills/spec/SKILL.md +107 -5
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Main assessment module for upstream analysis
|
|
3
|
+
* Coordinates release fetching, analysis, and output generation
|
|
4
|
+
*/
|
|
5
|
+
import { spawn } from "node:child_process";
|
|
6
|
+
import { readFile, writeFile, access, mkdir } from "node:fs/promises";
|
|
7
|
+
import { dirname, join } from "node:path";
|
|
8
|
+
import { analyzeRelease, getActionableFindings } from "./relevance.js";
|
|
9
|
+
import { calculateSummary, generateAssessmentReport, generateBatchedSummaryReport, generateLocalReport, } from "./report.js";
|
|
10
|
+
import { createAssessmentIssue, createOrLinkFinding } from "./issues.js";
|
|
11
|
+
/**
|
|
12
|
+
* Regex pattern for valid semantic version strings
|
|
13
|
+
* Matches: v1.2.3, 1.2.3, v1.2.3-beta.1, v1.2.3-rc1, etc.
|
|
14
|
+
*/
|
|
15
|
+
const VERSION_PATTERN = /^v?\d+\.\d+\.\d+(-[\w.]+)?$/;
|
|
16
|
+
/**
|
|
17
|
+
* Validate a version string to prevent command injection
|
|
18
|
+
* @throws Error if version format is invalid
|
|
19
|
+
*/
|
|
20
|
+
export function validateVersion(version) {
|
|
21
|
+
if (!VERSION_PATTERN.test(version)) {
|
|
22
|
+
throw new Error(`Invalid version format: "${version}". Expected semver format (e.g., v1.2.3 or 1.2.3-beta.1)`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Execute a command safely using spawn with argument arrays
|
|
27
|
+
* This prevents command injection by not using shell interpolation
|
|
28
|
+
*/
|
|
29
|
+
async function execCommand(command, args) {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
const proc = spawn(command, args, { stdio: ["pipe", "pipe", "pipe"] });
|
|
32
|
+
let stdout = "";
|
|
33
|
+
let stderr = "";
|
|
34
|
+
proc.stdout.on("data", (data) => {
|
|
35
|
+
stdout += data.toString();
|
|
36
|
+
});
|
|
37
|
+
proc.stderr.on("data", (data) => {
|
|
38
|
+
stderr += data.toString();
|
|
39
|
+
});
|
|
40
|
+
proc.on("close", (code) => {
|
|
41
|
+
if (code === 0) {
|
|
42
|
+
resolve({ stdout, stderr });
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
reject(new Error(`Command failed with exit code ${code}: ${stderr}`));
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
proc.on("error", (err) => {
|
|
49
|
+
reject(err);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Check if gh CLI is available and authenticated
|
|
55
|
+
* @returns Object with availability status and error message if not available
|
|
56
|
+
*/
|
|
57
|
+
export async function checkGhCliAvailable() {
|
|
58
|
+
try {
|
|
59
|
+
// Check if gh is installed
|
|
60
|
+
await execCommand("gh", ["--version"]);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return {
|
|
64
|
+
available: false,
|
|
65
|
+
authenticated: false,
|
|
66
|
+
error: "GitHub CLI (gh) is not installed. Install from: https://cli.github.com/",
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
try {
|
|
70
|
+
// Check if gh is authenticated
|
|
71
|
+
await execCommand("gh", ["auth", "status"]);
|
|
72
|
+
return { available: true, authenticated: true };
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
return {
|
|
76
|
+
available: true,
|
|
77
|
+
authenticated: false,
|
|
78
|
+
error: "GitHub CLI is not authenticated. Run: gh auth login",
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Default paths for upstream files
|
|
84
|
+
*/
|
|
85
|
+
const BASELINE_PATH = ".sequant/upstream/baseline.json";
|
|
86
|
+
const REPORTS_DIR = ".sequant/upstream";
|
|
87
|
+
/**
|
|
88
|
+
* Fetch release data from Claude Code repository
|
|
89
|
+
*/
|
|
90
|
+
export async function fetchRelease(version) {
|
|
91
|
+
try {
|
|
92
|
+
// Validate version if provided to prevent injection
|
|
93
|
+
if (version) {
|
|
94
|
+
validateVersion(version);
|
|
95
|
+
}
|
|
96
|
+
// Build args array safely - no shell interpolation
|
|
97
|
+
const args = ["release", "view"];
|
|
98
|
+
if (version) {
|
|
99
|
+
args.push(version);
|
|
100
|
+
}
|
|
101
|
+
args.push("--repo", "anthropics/claude-code", "--json", "tagName,name,body,publishedAt");
|
|
102
|
+
const { stdout } = await execCommand("gh", args);
|
|
103
|
+
return JSON.parse(stdout);
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
console.error("Error fetching release:", error);
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* List releases from Claude Code repository
|
|
112
|
+
*/
|
|
113
|
+
export async function listReleases(limit = 50) {
|
|
114
|
+
try {
|
|
115
|
+
// Validate limit is a reasonable positive integer
|
|
116
|
+
if (!Number.isInteger(limit) || limit < 1 || limit > 100) {
|
|
117
|
+
throw new Error("Limit must be an integer between 1 and 100");
|
|
118
|
+
}
|
|
119
|
+
const { stdout } = await execCommand("gh", [
|
|
120
|
+
"release",
|
|
121
|
+
"list",
|
|
122
|
+
"--repo",
|
|
123
|
+
"anthropics/claude-code",
|
|
124
|
+
"--limit",
|
|
125
|
+
String(limit),
|
|
126
|
+
"--json",
|
|
127
|
+
"tagName,publishedAt",
|
|
128
|
+
]);
|
|
129
|
+
return JSON.parse(stdout);
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
console.error("Error listing releases:", error);
|
|
133
|
+
return [];
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get releases since a specific version
|
|
138
|
+
*/
|
|
139
|
+
export async function getReleasesSince(sinceVersion) {
|
|
140
|
+
const releases = await listReleases();
|
|
141
|
+
const versions = [];
|
|
142
|
+
for (const release of releases) {
|
|
143
|
+
if (release.tagName === sinceVersion) {
|
|
144
|
+
break;
|
|
145
|
+
}
|
|
146
|
+
versions.push(release.tagName);
|
|
147
|
+
}
|
|
148
|
+
return versions.reverse(); // Oldest first
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Load baseline from file
|
|
152
|
+
*/
|
|
153
|
+
export async function loadBaseline(path = BASELINE_PATH) {
|
|
154
|
+
try {
|
|
155
|
+
const content = await readFile(path, "utf-8");
|
|
156
|
+
return JSON.parse(content);
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
// Return default baseline if file doesn't exist
|
|
160
|
+
console.warn("Baseline not found, using defaults");
|
|
161
|
+
return getDefaultBaseline();
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Save baseline to file
|
|
166
|
+
*/
|
|
167
|
+
export async function saveBaseline(baseline, path = BASELINE_PATH) {
|
|
168
|
+
await ensureDir(dirname(path));
|
|
169
|
+
await writeFile(path, JSON.stringify(baseline, null, 2));
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* Update baseline with new assessed version
|
|
173
|
+
*/
|
|
174
|
+
export async function updateBaseline(version, path = BASELINE_PATH) {
|
|
175
|
+
const baseline = await loadBaseline(path);
|
|
176
|
+
baseline.lastAssessedVersion = version;
|
|
177
|
+
await saveBaseline(baseline, path);
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Check if a version has already been assessed
|
|
181
|
+
*/
|
|
182
|
+
export async function isAlreadyAssessed(version) {
|
|
183
|
+
const reportPath = join(REPORTS_DIR, `${version}.md`);
|
|
184
|
+
try {
|
|
185
|
+
await access(reportPath);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
/**
|
|
193
|
+
* Save local report
|
|
194
|
+
*/
|
|
195
|
+
export async function saveLocalReport(assessment) {
|
|
196
|
+
const reportPath = join(REPORTS_DIR, `${assessment.version}.md`);
|
|
197
|
+
await ensureDir(REPORTS_DIR);
|
|
198
|
+
const content = generateLocalReport(assessment);
|
|
199
|
+
await writeFile(reportPath, content);
|
|
200
|
+
return reportPath;
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Run a single version assessment
|
|
204
|
+
*/
|
|
205
|
+
export async function assessVersion(version, options = {}) {
|
|
206
|
+
const { dryRun = false, force = false } = options;
|
|
207
|
+
// Check if already assessed
|
|
208
|
+
if (!force && (await isAlreadyAssessed(version))) {
|
|
209
|
+
console.log(`Already assessed: ${version}. Use --force to re-assess.`);
|
|
210
|
+
return null;
|
|
211
|
+
}
|
|
212
|
+
// Fetch release
|
|
213
|
+
const release = await fetchRelease(version);
|
|
214
|
+
if (!release) {
|
|
215
|
+
console.error(`Failed to fetch release: ${version}`);
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
// Load baseline
|
|
219
|
+
const baseline = await loadBaseline();
|
|
220
|
+
// Analyze
|
|
221
|
+
const findings = analyzeRelease(release.body, baseline);
|
|
222
|
+
const actionableFindings = getActionableFindings(findings);
|
|
223
|
+
// Create assessment object
|
|
224
|
+
const assessment = {
|
|
225
|
+
version: release.tagName,
|
|
226
|
+
releaseDate: release.publishedAt.split("T")[0],
|
|
227
|
+
assessmentDate: new Date().toISOString().split("T")[0],
|
|
228
|
+
previousVersion: baseline.lastAssessedVersion,
|
|
229
|
+
findings,
|
|
230
|
+
issuesCreated: [],
|
|
231
|
+
summary: calculateSummary(findings),
|
|
232
|
+
dryRun,
|
|
233
|
+
};
|
|
234
|
+
// Create assessment issue first (to get issue number for linking)
|
|
235
|
+
const assessmentBody = generateAssessmentReport(assessment);
|
|
236
|
+
const assessmentIssueNumber = await createAssessmentIssue(`Upstream: Claude Code ${release.tagName} Assessment`, assessmentBody, dryRun);
|
|
237
|
+
// Create issues for actionable findings
|
|
238
|
+
for (let i = 0; i < actionableFindings.length; i++) {
|
|
239
|
+
const updatedFinding = await createOrLinkFinding(actionableFindings[i], release.tagName, assessmentIssueNumber, dryRun);
|
|
240
|
+
// Update in original findings array
|
|
241
|
+
const originalIndex = findings.findIndex((f) => f.description === updatedFinding.description);
|
|
242
|
+
if (originalIndex >= 0) {
|
|
243
|
+
findings[originalIndex] = updatedFinding;
|
|
244
|
+
}
|
|
245
|
+
if (updatedFinding.issueNumber) {
|
|
246
|
+
assessment.issuesCreated.push(updatedFinding.issueNumber);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
// Save local report
|
|
250
|
+
await saveLocalReport(assessment);
|
|
251
|
+
// Update baseline
|
|
252
|
+
if (!dryRun) {
|
|
253
|
+
await updateBaseline(release.tagName);
|
|
254
|
+
}
|
|
255
|
+
return assessment;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Run assessment for latest release
|
|
259
|
+
*/
|
|
260
|
+
export async function assessLatest(options = {}) {
|
|
261
|
+
const release = await fetchRelease();
|
|
262
|
+
if (!release) {
|
|
263
|
+
console.error("Failed to fetch latest release");
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
return assessVersion(release.tagName, options);
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* Run batched assessment for multiple versions
|
|
270
|
+
*/
|
|
271
|
+
export async function assessSince(sinceVersion, options = {}) {
|
|
272
|
+
const { dryRun = false } = options;
|
|
273
|
+
const versions = await getReleasesSince(sinceVersion);
|
|
274
|
+
if (versions.length === 0) {
|
|
275
|
+
console.log(`No new versions since ${sinceVersion}`);
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
console.log(`Found ${versions.length} versions to assess: ${versions.join(", ")}`);
|
|
279
|
+
const assessments = [];
|
|
280
|
+
for (const version of versions) {
|
|
281
|
+
const assessment = await assessVersion(version, {
|
|
282
|
+
...options,
|
|
283
|
+
force: true,
|
|
284
|
+
});
|
|
285
|
+
if (assessment) {
|
|
286
|
+
assessments.push(assessment);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
if (assessments.length === 0) {
|
|
290
|
+
return null;
|
|
291
|
+
}
|
|
292
|
+
const batched = {
|
|
293
|
+
versions,
|
|
294
|
+
assessments,
|
|
295
|
+
sinceVersion,
|
|
296
|
+
toVersion: versions[versions.length - 1],
|
|
297
|
+
};
|
|
298
|
+
// Create summary issue
|
|
299
|
+
if (!dryRun) {
|
|
300
|
+
const summaryBody = generateBatchedSummaryReport(batched);
|
|
301
|
+
const summaryIssue = await createAssessmentIssue(`Upstream: Claude Code Assessment (${sinceVersion} → ${batched.toVersion})`, summaryBody, dryRun);
|
|
302
|
+
batched.summaryIssueNumber = summaryIssue;
|
|
303
|
+
}
|
|
304
|
+
return batched;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Main entry point for upstream assessment
|
|
308
|
+
*/
|
|
309
|
+
export async function runUpstream(options = {}) {
|
|
310
|
+
const { version, since } = options;
|
|
311
|
+
if (since) {
|
|
312
|
+
return assessSince(since, options);
|
|
313
|
+
}
|
|
314
|
+
if (version) {
|
|
315
|
+
return assessVersion(version, options);
|
|
316
|
+
}
|
|
317
|
+
return assessLatest(options);
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Get default baseline when none exists
|
|
321
|
+
*/
|
|
322
|
+
function getDefaultBaseline() {
|
|
323
|
+
return {
|
|
324
|
+
lastAssessedVersion: null,
|
|
325
|
+
schemaVersion: "1.0.0",
|
|
326
|
+
tools: {
|
|
327
|
+
core: [
|
|
328
|
+
"Task",
|
|
329
|
+
"Bash",
|
|
330
|
+
"Read",
|
|
331
|
+
"Write",
|
|
332
|
+
"Edit",
|
|
333
|
+
"Glob",
|
|
334
|
+
"Grep",
|
|
335
|
+
"TodoWrite",
|
|
336
|
+
],
|
|
337
|
+
optional: ["WebFetch", "WebSearch", "NotebookEdit", "AskUserQuestion"],
|
|
338
|
+
},
|
|
339
|
+
hooks: {
|
|
340
|
+
used: ["PreToolUse"],
|
|
341
|
+
files: ["src/hooks/pre-tool-hook.ts"],
|
|
342
|
+
},
|
|
343
|
+
mcpServers: {
|
|
344
|
+
required: [],
|
|
345
|
+
optional: ["chrome-devtools", "context7", "sequential-thinking"],
|
|
346
|
+
},
|
|
347
|
+
permissions: {
|
|
348
|
+
patterns: ["Bash(*)", "Task(*)", "Edit(*)"],
|
|
349
|
+
files: [".claude/settings.json"],
|
|
350
|
+
},
|
|
351
|
+
keywords: [
|
|
352
|
+
"Task",
|
|
353
|
+
"Bash",
|
|
354
|
+
"hook",
|
|
355
|
+
"PreToolUse",
|
|
356
|
+
"PostToolUse",
|
|
357
|
+
"MCP",
|
|
358
|
+
"permission",
|
|
359
|
+
"allow",
|
|
360
|
+
"deny",
|
|
361
|
+
"tool",
|
|
362
|
+
"background",
|
|
363
|
+
"parallel",
|
|
364
|
+
"agent",
|
|
365
|
+
"subagent",
|
|
366
|
+
],
|
|
367
|
+
dependencyMap: {
|
|
368
|
+
permission: [".claude/settings.json"],
|
|
369
|
+
hook: ["src/hooks/pre-tool-hook.ts"],
|
|
370
|
+
Task: [".claude/skills/**/*.md"],
|
|
371
|
+
MCP: [".claude/settings.json"],
|
|
372
|
+
},
|
|
373
|
+
};
|
|
374
|
+
}
|
|
375
|
+
/**
|
|
376
|
+
* Ensure directory exists
|
|
377
|
+
*/
|
|
378
|
+
async function ensureDir(dir) {
|
|
379
|
+
try {
|
|
380
|
+
await mkdir(dir, { recursive: true });
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
// Directory may already exist
|
|
384
|
+
}
|
|
385
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream Assessment Module
|
|
3
|
+
*
|
|
4
|
+
* Monitors Claude Code releases and assesses compatibility with sequant.
|
|
5
|
+
* Auto-creates GitHub issues for feature opportunities and breaking changes.
|
|
6
|
+
*/
|
|
7
|
+
export { runUpstream, assessVersion, assessLatest, assessSince, fetchRelease, listReleases, getReleasesSince, loadBaseline, saveBaseline, updateBaseline, isAlreadyAssessed, saveLocalReport, validateVersion, checkGhCliAvailable, } from "./assessment.js";
|
|
8
|
+
export { extractChanges, matchKeywords, matchPatterns, categorizeChange, determineImpact, getImpactFiles, generateTitle, analyzeChange, analyzeRelease, getActionableFindings, DEFAULT_PATTERNS, } from "./relevance.js";
|
|
9
|
+
export { calculateSummary, generateAssessmentReport, generateFindingIssue, generateBatchedSummaryReport, generateLocalReport, } from "./report.js";
|
|
10
|
+
export { checkForDuplicate, createIssue, addIssueComment, createOrLinkFinding, createAssessmentIssue, extractSearchTerms, isSimilarTitle, } from "./issues.js";
|
|
11
|
+
export type { FindingCategory, ImpactLevel, Finding, AssessmentSummary, UpstreamAssessment, ReleaseData, ToolsConfig, HooksConfig, McpServersConfig, PermissionsConfig, Baseline, DetectionPatterns, AssessmentOptions, DuplicateCheckResult, IssueParams, IssueResult, BatchedAssessment, } from "./types.js";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Upstream Assessment Module
|
|
3
|
+
*
|
|
4
|
+
* Monitors Claude Code releases and assesses compatibility with sequant.
|
|
5
|
+
* Auto-creates GitHub issues for feature opportunities and breaking changes.
|
|
6
|
+
*/
|
|
7
|
+
// Main exports
|
|
8
|
+
export { runUpstream, assessVersion, assessLatest, assessSince, fetchRelease, listReleases, getReleasesSince, loadBaseline, saveBaseline, updateBaseline, isAlreadyAssessed, saveLocalReport, validateVersion, checkGhCliAvailable, } from "./assessment.js";
|
|
9
|
+
// Relevance detection
|
|
10
|
+
export { extractChanges, matchKeywords, matchPatterns, categorizeChange, determineImpact, getImpactFiles, generateTitle, analyzeChange, analyzeRelease, getActionableFindings, DEFAULT_PATTERNS, } from "./relevance.js";
|
|
11
|
+
// Report generation
|
|
12
|
+
export { calculateSummary, generateAssessmentReport, generateFindingIssue, generateBatchedSummaryReport, generateLocalReport, } from "./report.js";
|
|
13
|
+
// Issue management
|
|
14
|
+
export { checkForDuplicate, createIssue, addIssueComment, createOrLinkFinding, createAssessmentIssue, extractSearchTerms, isSimilarTitle, } from "./issues.js";
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub issue management for upstream assessments
|
|
3
|
+
* Handles issue creation, deduplication, and commenting
|
|
4
|
+
*
|
|
5
|
+
* Security: All gh CLI calls use spawn() with argument arrays to prevent
|
|
6
|
+
* command injection. No shell interpolation is used.
|
|
7
|
+
*/
|
|
8
|
+
import type { DuplicateCheckResult, Finding, IssueParams, IssueResult } from "./types.js";
|
|
9
|
+
/**
|
|
10
|
+
* Check if a similar upstream issue already exists
|
|
11
|
+
*/
|
|
12
|
+
export declare function checkForDuplicate(title: string, owner?: string, repo?: string): Promise<DuplicateCheckResult>;
|
|
13
|
+
/**
|
|
14
|
+
* Extract search terms from a title
|
|
15
|
+
* Removes common words and version info
|
|
16
|
+
*/
|
|
17
|
+
export declare function extractSearchTerms(title: string): string;
|
|
18
|
+
/**
|
|
19
|
+
* Check if two titles are similar enough to be duplicates
|
|
20
|
+
*/
|
|
21
|
+
export declare function isSimilarTitle(title1: string, title2: string): boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Create a GitHub issue using a temporary file for the body
|
|
24
|
+
* This avoids any shell escaping issues with complex markdown content
|
|
25
|
+
*/
|
|
26
|
+
export declare function createIssue(params: IssueParams, owner?: string, repo?: string): Promise<IssueResult>;
|
|
27
|
+
/**
|
|
28
|
+
* Add a comment to an existing issue
|
|
29
|
+
*/
|
|
30
|
+
export declare function addIssueComment(issueNumber: number, comment: string, owner?: string, repo?: string): Promise<void>;
|
|
31
|
+
/**
|
|
32
|
+
* Create or link an issue for a finding
|
|
33
|
+
*/
|
|
34
|
+
export declare function createOrLinkFinding(finding: Finding, version: string, assessmentIssueNumber: number | undefined, dryRun?: boolean, owner?: string, repo?: string): Promise<Finding>;
|
|
35
|
+
/**
|
|
36
|
+
* Create the assessment summary issue
|
|
37
|
+
*/
|
|
38
|
+
export declare function createAssessmentIssue(title: string, body: string, dryRun?: boolean, owner?: string, repo?: string): Promise<number | undefined>;
|