@staff0rd/assist 0.293.0 → 0.294.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/commands/pr.md +50 -2
- package/dist/index.js +48 -1
- package/package.json +1 -1
package/claude/commands/pr.md
CHANGED
|
@@ -12,9 +12,57 @@ Use `assist prs raise` to create the PR — do not use `gh pr create` directly.
|
|
|
12
12
|
- `--how <how>` — optional; how the change works (rendered as `## How`). Omit it unless the approach genuinely needs explaining.
|
|
13
13
|
- `--resolves <key>` — Jira issue key resolved by this PR; repeatable. Each key's browse URL is appended inline to the `## Why` section. Unless a Jira key is already known from the session (e.g. mentioned by the user or present on the branch), ask the user whether this PR resolves a Jira issue and for the key before raising; omit `--resolves` only if they say there isn't one.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Wrap symbols, file paths, function names, class names, variable names, config keys, CLI commands, and flag names in backticks.
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
One section, one question. Each section answers exactly one thing, and implementation detail lives only in `## How`:
|
|
18
|
+
|
|
19
|
+
- `## What` — what is observably different for someone using or calling this, and nothing else. Not how it's built, not which functions/files changed.
|
|
20
|
+
- `## Why` — the problem or motivation that made the change worth doing. Not how the solution works.
|
|
21
|
+
- `## How` — only the non-obvious decisions the diff alone won't explain: a deliberate trade-off, a workaround, a reason the obvious approach was rejected. Omit it entirely by default. Never restate `## What` as mechanism, and never walk through the diff.
|
|
22
|
+
|
|
23
|
+
The most common failure is altitude bleed: the author has an implementation detail they want to include, so they spray it into whichever section they're writing — `## What` narrates the diff, `## How` restates `## What`, `## Why` smuggles in mechanism. Don't. If a sentence describes a mechanism, it belongs in `## How` — and probably shouldn't exist at all unless it's a genuine non-obvious decision.
|
|
24
|
+
|
|
25
|
+
Litmus — a sentence is almost certainly mechanism (so `## How`, or cut it) if it contains "by …ing", "so that it can", "because the X filters/needs/uses", or names an internal component, property, function, or file.
|
|
26
|
+
|
|
27
|
+
Brevity budget — keep each section within these limits:
|
|
28
|
+
|
|
29
|
+
- `## What` — a few sentences (2–3).
|
|
30
|
+
- `## Why` — 1–2 sentences.
|
|
31
|
+
- `## How` — omit by default; a sentence or two, only for non-obvious decisions, not a diff walkthrough.
|
|
32
|
+
|
|
33
|
+
Write prose. A short paragraph of a few sentences is the natural form for `## What` and `## Why` — bullets in `## What` are a smell, usually a sign the change is being narrated point-by-point like a diff. Use bullets only when there are genuinely several independent, parallel items (3+) that don't read as a paragraph; a single bullet is never right — make it a sentence. Don't pack multiple points into one long running paragraph either: a single non-list paragraph over ~600 characters or ~4 sentences is a wall of text and `assist prs raise`/`assist prs edit` will reject it.
|
|
34
|
+
|
|
35
|
+
Worked example — altitude bleed across every section.
|
|
36
|
+
|
|
37
|
+
Bad — mechanism leaks everywhere:
|
|
38
|
+
|
|
39
|
+
> ## What
|
|
40
|
+
>
|
|
41
|
+
> Adds a `RetryPolicy` wrapper that catches transient errors and re-invokes the handler with exponential backoff so callers don't see intermittent failures.
|
|
42
|
+
>
|
|
43
|
+
> ## Why
|
|
44
|
+
>
|
|
45
|
+
> Intermittent failures were surfacing to users. The handler also needs to distinguish transient from permanent errors, which the new `isTransient` check does by inspecting the status code.
|
|
46
|
+
>
|
|
47
|
+
> ## How
|
|
48
|
+
>
|
|
49
|
+
> The wrapper retries with exponential backoff on transient errors.
|
|
50
|
+
|
|
51
|
+
`## What` describes the implementation (a wrapper, backoff) rather than the observable change. `## Why`'s second sentence is mechanism dressed as motivation. `## How` just restates `## What` at a lower altitude. Good — each section stays at its own altitude:
|
|
52
|
+
|
|
53
|
+
> ## What
|
|
54
|
+
>
|
|
55
|
+
> Intermittent failures are now retried automatically instead of surfacing to the user.
|
|
56
|
+
>
|
|
57
|
+
> ## Why
|
|
58
|
+
>
|
|
59
|
+
> Transient errors were failing requests that would have succeeded on a second attempt.
|
|
60
|
+
>
|
|
61
|
+
> ## How
|
|
62
|
+
>
|
|
63
|
+
> Only errors flagged transient are retried; permanent failures still fail fast, to avoid masking real bugs behind retries.
|
|
64
|
+
|
|
65
|
+
Only the genuinely non-obvious decision survives in `## How`; everything that merely paraphrased `## What` is gone.
|
|
18
66
|
|
|
19
67
|
`assist prs raise` errors if a pull request already exists for the branch. Pass `--force` to fully overwrite the existing PR's title and body.
|
|
20
68
|
|
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.294.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -11327,6 +11327,45 @@ ${resolves}` : baseWhy);
|
|
|
11327
11327
|
}
|
|
11328
11328
|
|
|
11329
11329
|
// src/commands/prs/validatePrContent.ts
|
|
11330
|
+
var MAX_PARAGRAPH_CHARS = 600;
|
|
11331
|
+
var MAX_PARAGRAPH_SENTENCES = 4;
|
|
11332
|
+
function isListLine(line) {
|
|
11333
|
+
return /^\s*([-*+]|\d+[.)])\s/.test(line);
|
|
11334
|
+
}
|
|
11335
|
+
function countSentences(paragraph) {
|
|
11336
|
+
return paragraph.match(/[.!?]+(\s|$)/g)?.length ?? 0;
|
|
11337
|
+
}
|
|
11338
|
+
function splitParagraphs(body) {
|
|
11339
|
+
const paragraphs = [];
|
|
11340
|
+
let section2 = "(intro)";
|
|
11341
|
+
let lines = [];
|
|
11342
|
+
const flush = () => {
|
|
11343
|
+
if (lines.length > 0) {
|
|
11344
|
+
paragraphs.push({ section: section2, lines });
|
|
11345
|
+
lines = [];
|
|
11346
|
+
}
|
|
11347
|
+
};
|
|
11348
|
+
for (const line of body.split("\n")) {
|
|
11349
|
+
const heading = line.match(/^#{1,6}\s+(.*)$/);
|
|
11350
|
+
if (heading) {
|
|
11351
|
+
flush();
|
|
11352
|
+
section2 = heading[1].trim();
|
|
11353
|
+
} else if (line.trim() === "") {
|
|
11354
|
+
flush();
|
|
11355
|
+
} else {
|
|
11356
|
+
lines.push(line);
|
|
11357
|
+
}
|
|
11358
|
+
}
|
|
11359
|
+
flush();
|
|
11360
|
+
return paragraphs;
|
|
11361
|
+
}
|
|
11362
|
+
function isWallOfText(lines) {
|
|
11363
|
+
if (lines.some(isListLine)) {
|
|
11364
|
+
return false;
|
|
11365
|
+
}
|
|
11366
|
+
const text3 = lines.join(" ").trim();
|
|
11367
|
+
return text3.length > MAX_PARAGRAPH_CHARS || countSentences(text3) > MAX_PARAGRAPH_SENTENCES;
|
|
11368
|
+
}
|
|
11330
11369
|
function validatePrContent(title, body) {
|
|
11331
11370
|
if (title.toLowerCase().includes("claude")) {
|
|
11332
11371
|
console.error("Error: PR title must not reference Claude");
|
|
@@ -11336,6 +11375,14 @@ function validatePrContent(title, body) {
|
|
|
11336
11375
|
console.error("Error: PR body must not reference Claude");
|
|
11337
11376
|
process.exit(1);
|
|
11338
11377
|
}
|
|
11378
|
+
const wall = splitParagraphs(body).find((p) => isWallOfText(p.lines));
|
|
11379
|
+
if (wall) {
|
|
11380
|
+
const chars = wall.lines.join(" ").trim().length;
|
|
11381
|
+
console.error(
|
|
11382
|
+
`Error: the "${wall.section}" section contains a wall-of-text paragraph (${chars} chars). Be concise \u2014 split it into bullet points or trim it.`
|
|
11383
|
+
);
|
|
11384
|
+
process.exit(1);
|
|
11385
|
+
}
|
|
11339
11386
|
}
|
|
11340
11387
|
|
|
11341
11388
|
// src/commands/prs/edit.ts
|