@erclx/aitk 0.81.0 → 0.82.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/.claude-plugin/plugin.json +1 -1
- package/claude/skills/git-pr/REQUIREMENT.md +8 -0
- package/claude/skills/git-pr/SKILL.md +20 -1
- package/claude/skills/git-pr/references/labels.md +59 -0
- package/docs/agents/markdown-audit.md +3 -1
- package/package.json +1 -1
- package/src/markdown/structure.ts +7 -1
|
@@ -9,6 +9,8 @@ description: What pull request generation is for, the gaps it closes, and what i
|
|
|
9
9
|
|
|
10
10
|
Without this skill, a pull request body is written from memory of the branch rather than from its diff, so it describes the intent and omits what the work turned into. Testing boxes get ticked from intent, which records what was meant to run instead of what ran, and a reviewer trusts the list. A second push either errors on create or opens a duplicate pull request, and banned characters survive into a body the hook never sees.
|
|
11
11
|
|
|
12
|
+
Every pull request also lands unlabelled, so a merged list is a wall of titles with no way to filter it by surface. A reader looking for what changed in one domain reads all of them, and the conventional commit type in the title says what kind of change it is rather than where.
|
|
13
|
+
|
|
12
14
|
A lookup that resolves by head branch alone carries its own failure. A branch name reused after an earlier pull request merged resolves to the closed one, so the run rewrites a merged pull request's title and body and reports its URL as the one it opened. Both fields are recoverable only through the issue timeline and the edit history, and nothing reports that the write landed on the wrong object.
|
|
13
15
|
|
|
14
16
|
## Must
|
|
@@ -21,6 +23,9 @@ A lookup that resolves by head branch alone carries its own failure. A branch na
|
|
|
21
23
|
- Detect an open pull request and edit it in place, so a follow-up push keeps the body in sync instead of failing
|
|
22
24
|
- Scope that detection to an open pull request on the current head and the default base, so neither a reused branch name nor a second base resolves the wrong one
|
|
23
25
|
- Resolve the pull request once and reuse what that resolution returned, so the number recorded never depends on how a lookup ranks two pull requests sharing a head
|
|
26
|
+
- Label from the paths the branch changed, against a map the project declares, so the label set belongs to the project rather than to the skill
|
|
27
|
+
- Apply labels after the pull request exists, so a label the remote does not carry costs a warning rather than the pull request
|
|
28
|
+
- Report a refused label, since a warning nothing surfaces leaves the run indistinguishable from one that labelled
|
|
24
29
|
|
|
25
30
|
## Must not
|
|
26
31
|
|
|
@@ -28,12 +33,15 @@ A lookup that resolves by head branch alone carries its own failure. A branch na
|
|
|
28
33
|
- Put a request for the reviewer in the Testing list, since a request is not a result
|
|
29
34
|
- Create a second pull request when one is open
|
|
30
35
|
- Edit a pull request that is not open, or record its number on a task
|
|
36
|
+
- Name a domain of any one project in the skill body or its references
|
|
37
|
+
- Create a label the map names and the remote lacks
|
|
31
38
|
- Emit anything after the result line
|
|
32
39
|
|
|
33
40
|
## Guards
|
|
34
41
|
|
|
35
42
|
- Branch name does not conform: stop and route to the skill that renames
|
|
36
43
|
- No commits ahead of main: stop
|
|
44
|
+
- No label map in the project: label nothing and warn nothing, since an absent map is a decision rather than a gap
|
|
37
45
|
|
|
38
46
|
## Out of scope
|
|
39
47
|
|
|
@@ -11,6 +11,7 @@ Read these files in parallel:
|
|
|
11
11
|
|
|
12
12
|
- `${CLAUDE_SKILL_DIR}/references/branch.md`: branch format, valid types, and constraints
|
|
13
13
|
- `${CLAUDE_SKILL_DIR}/references/pr.md`: structure, rules, and banned phrases
|
|
14
|
+
- `${CLAUDE_SKILL_DIR}/references/labels.md`: label map format, matching, and the missing-label warning. Skip when the project has no `.claude/pr-labels.toml`.
|
|
14
15
|
- `.claude/standards/prose.md` from the project root: voice and banned words for all generated text
|
|
15
16
|
- `.claude/standards/markdown.md` from the project root: punctuation and formatting for all generated text
|
|
16
17
|
- `.claude/standards/versioning.md` from the project root: phase label vs semver discipline
|
|
@@ -29,6 +30,7 @@ Then run these commands in parallel to gather git context:
|
|
|
29
30
|
- `git branch --show-current 2>/dev/null || echo "unknown"`
|
|
30
31
|
- `git log <base>..HEAD --oneline 2>/dev/null || echo "NO_COMMITS"`
|
|
31
32
|
- `git diff <base> HEAD -- . ':(exclude)*.lock' ':(exclude)*-lock.json' 2>/dev/null || echo "NO_DIFF"`
|
|
33
|
+
- `git diff --name-only <base> HEAD 2>/dev/null || echo "NO_FILES"`
|
|
32
34
|
|
|
33
35
|
## Diff baseline
|
|
34
36
|
|
|
@@ -82,15 +84,24 @@ The lookup scopes to the base as well as the head. One head can carry open pull
|
|
|
82
84
|
|
|
83
85
|
A detached HEAD gives `git branch --show-current` an empty result, which would read as no open pull request and create a second one. The branch-name guard above stops the run first, since an empty name does not match `<type>/<description>`.
|
|
84
86
|
|
|
87
|
+
### Labels
|
|
88
|
+
|
|
89
|
+
Read `.claude/pr-labels.toml` from the project root. A project that has not declared a map gets no labels and no warning, since a label set this skill supplied would be a guess about that project's surfaces.
|
|
90
|
+
|
|
91
|
+
When the file resolves, match it against the name-only diff per `${CLAUDE_SKILL_DIR}/references/labels.md` and write the comma-separated result into `pr_labels` below. Leave `pr_labels` empty when no map resolves or no prefix matches, which skips the labelling command rather than running it against nothing.
|
|
92
|
+
|
|
85
93
|
### Final command
|
|
86
94
|
|
|
87
95
|
Detect an open pull request on the current head and branch: edit it in place when one exists, create it otherwise. This keeps the body in sync on a follow-up push instead of erroring on `gh pr create`.
|
|
88
96
|
|
|
97
|
+
Labels apply after that branch converges, against a pull request that already exists. `gh pr create --label` refuses a label the remote does not carry and opens no pull request at all, so a mistyped row costs the run rather than the label. One command after the fact also covers the create and the edit path together.
|
|
98
|
+
|
|
89
99
|
```bash
|
|
90
100
|
mkdir -p .claude/.tmp/pr
|
|
91
101
|
cat <<'BODY' > .claude/.tmp/pr/body.md
|
|
92
102
|
<body content following pr.md template exactly>
|
|
93
103
|
BODY
|
|
104
|
+
pr_labels="<comma-separated labels, empty when the map resolves to nothing>"
|
|
94
105
|
git push -u origin HEAD || exit 1
|
|
95
106
|
base_branch=$(gh repo view --json defaultBranchRef --jq .defaultBranchRef.name) || exit 1
|
|
96
107
|
pr_number=$(gh pr list --head "$(git branch --show-current)" --base "$base_branch" --state open --json number --jq '.[0].number // empty')
|
|
@@ -100,6 +111,10 @@ else
|
|
|
100
111
|
pr_url=$(gh pr create --title "<title>" --body-file .claude/.tmp/pr/body.md) || exit 1
|
|
101
112
|
pr_number=${pr_url##*/}
|
|
102
113
|
fi
|
|
114
|
+
if [ -n "$pr_labels" ]; then
|
|
115
|
+
gh pr edit "$pr_number" --add-label "$pr_labels" >/dev/null ||
|
|
116
|
+
printf 'Label apply failed. Create a missing label with: gh label create <name>\n' >&2
|
|
117
|
+
fi
|
|
103
118
|
rm -rf .claude/.tmp/pr
|
|
104
119
|
printf 'number=%s\nurl=%s\n' "$pr_number" "$pr_url"
|
|
105
120
|
```
|
|
@@ -124,8 +139,12 @@ The number is what lets the merge close the task. Every merge on `main` is a squ
|
|
|
124
139
|
|
|
125
140
|
## After execution
|
|
126
141
|
|
|
127
|
-
Respond with
|
|
142
|
+
Respond with one line, using the `url` the final command printed:
|
|
128
143
|
|
|
129
144
|
`✅ PR: <url>`
|
|
130
145
|
|
|
146
|
+
Add a second line only when the labelling command printed its warning, quoting the label `gh` refused:
|
|
147
|
+
|
|
148
|
+
`⚠️ Labels not applied: <what gh reported>`
|
|
149
|
+
|
|
131
150
|
Do not add any other text.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Label reference
|
|
3
|
+
description: Path-to-label map format, prefix matching against the changed set, and the one-time label creation a map requires
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Label reference
|
|
7
|
+
|
|
8
|
+
## Scope
|
|
9
|
+
|
|
10
|
+
Governs the labels a pull request carries: the map a project declares, how a changed path resolves to a label, and what a label missing from the remote costs.
|
|
11
|
+
|
|
12
|
+
Does not govern:
|
|
13
|
+
|
|
14
|
+
- Pull request title and body: `pr.md`
|
|
15
|
+
- Branch naming: `branch.md`
|
|
16
|
+
- Issue labels, which `git-issue` derives from the issue type rather than from a diff
|
|
17
|
+
|
|
18
|
+
## Map format
|
|
19
|
+
|
|
20
|
+
The map lives at `.claude/pr-labels.toml` in the project root. Each key under `[domains]` is a label name and its value is the list of path prefixes that earn it.
|
|
21
|
+
|
|
22
|
+
```toml
|
|
23
|
+
[domains]
|
|
24
|
+
api = ["services/api/"]
|
|
25
|
+
web = ["apps/web/", "packages/ui/"]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
A label takes more than one prefix when two folders read as one surface. Two labels may claim overlapping prefixes, and a path under both earns both.
|
|
29
|
+
|
|
30
|
+
The map is authored by hand and nothing detects a directory it fails to cover, so a surface added after the map was written labels nothing until someone adds a row.
|
|
31
|
+
|
|
32
|
+
## Matching
|
|
33
|
+
|
|
34
|
+
- Take the changed set from `git diff --name-only <base> HEAD`, resolved against the same base as the diff the body is written from
|
|
35
|
+
- A path earns a label when the path starts with one of that label's prefixes
|
|
36
|
+
- Collect the distinct labels across the whole set, ordered as the map declares them, so two runs over one branch produce one string
|
|
37
|
+
- Pass the result as a single comma-separated value. An empty result runs no labelling step.
|
|
38
|
+
|
|
39
|
+
## Applying
|
|
40
|
+
|
|
41
|
+
Apply labels after the pull request resolves, never as a flag on the create. `gh pr create --label` fails whole on a label the remote does not carry, so a name the map got wrong opens no pull request at all and the run stops with the branch pushed and nothing to review. A `gh pr edit --add-label` against a pull request that already exists costs a warning instead, and it is one command across both the create and the edit path rather than two flags that have to stay in step.
|
|
42
|
+
|
|
43
|
+
`--add-label` adds and never removes. A label a person applied by hand is not this skill's to strip, so a domain that stops applying between two pushes keeps its label until someone takes it off.
|
|
44
|
+
|
|
45
|
+
## A label the remote does not carry
|
|
46
|
+
|
|
47
|
+
Warn and continue rather than creating it, naming the command in the warning:
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gh label create <name> --description "<text>"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Creating a label writes to the repository settings from a run the user invoked to open a pull request, and a label created from a typo in the map is harder to notice than a warning is.
|
|
54
|
+
|
|
55
|
+
The refusal names the label it rejected and applies none of the set, so a warning that reaches nobody leaves the run reading exactly like one that labelled. Surface it beside the result line rather than letting the pull request URL stand alone.
|
|
56
|
+
|
|
57
|
+
## Release pull requests
|
|
58
|
+
|
|
59
|
+
Release automation opens its own pull requests without this skill and applies its own labels, so nothing here needs a skip condition for them.
|
|
@@ -55,7 +55,9 @@ Every weight and depth measure counts the text a reader is shown. A link reduces
|
|
|
55
55
|
|
|
56
56
|
A code span is walked around rather than through, so a path quoting link or angle-bracket syntax keeps the width the page gives it. Masking inside one takes back the decision to count it, and the placeholders this toolkit writes are where that shows.
|
|
57
57
|
|
|
58
|
-
The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones:
|
|
58
|
+
The paragraph check measures both halves of one rule. `markdown.md` caps a paragraph at four sentences, and a sentence cap on its own is satisfied by writing fewer and longer ones: 15 paragraphs in this corpus sit inside four sentences and past the weight checkpoint, and the heaviest of those runs 886 characters. The standard therefore states a weight beside the sentence cap, and the verb reads it as its own checkpoint.
|
|
59
|
+
|
|
60
|
+
A sentence boundary closes on terminal punctuation ahead of a capital or a code span. The capital is what keeps a version pin and a decimal from each reading as two sentences, and the code span is admitted beside it because a command name opening a sentence carries no capital to find.
|
|
59
61
|
|
|
60
62
|
The paragraph weight sits at 700 and the bullet weight at 400. Both shipped at 400, because the paragraph number was borrowed from the bullet rule when the two checks landed together, and each has since been read against a sample of its own. They are separate checkpoints in the standard and separate patterns in the parser, so a read that moves one leaves the other where it is.
|
|
61
63
|
|
package/package.json
CHANGED
|
@@ -15,8 +15,14 @@ const BLOCKQUOTE = /^\s*>/
|
|
|
15
15
|
* one. An abbreviation ahead of a capitalized word still counts, which
|
|
16
16
|
* over-reports by one on the sentence that carries it and is why this measure
|
|
17
17
|
* reports rather than gates.
|
|
18
|
+
*
|
|
19
|
+
* A code span opens a sentence that carries no capital at all, since a command
|
|
20
|
+
* name is lowercase and the requirement above would catch nothing there. The
|
|
21
|
+
* backtick therefore stands alone rather than joining the delimiters a capital
|
|
22
|
+
* follows, and the price is that a span opening a fragment mid-paragraph reads
|
|
23
|
+
* as a sentence start.
|
|
18
24
|
*/
|
|
19
|
-
const SENTENCE_END = /[.!?]["'’”)\]]*(?=\s+["'“(\[]*[A-Z]|\s*$)/g
|
|
25
|
+
const SENTENCE_END = /[.!?]["'’”)\]]*(?=\s+(?:["'“(\[]*[A-Z]|`)|\s*$)/g
|
|
20
26
|
|
|
21
27
|
const NUMBER_WORDS: Record<string, number> = {
|
|
22
28
|
one: 1,
|