skilldrop-cli 0.16.31 → 0.16.33
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/bin/skilldrop.js +133 -18
- package/contracts/skill.schema.json +3 -3
- package/package.json +1 -1
- package/packs/converters/skills/file-to-markdown/SKILL.md +8 -4
- package/packs/converters/skills/file-to-markdown/manifest.json +11 -2
- package/packs/converters/skills/file-to-markdown/scripts/to_markdown.py +118 -8
- package/packs/converters/skills/md-to-docx/examples/q3-support-review.md +7 -0
- package/packs/converters/skills/md-to-xlsx/examples/vendor-review.md +6 -1
- package/packs/converters/skills/mermaid-render/manifest.json +11 -1
- package/packs/dev-team/skills/codebase-tour/evals/files/3/.github/workflows/ci.yml +2 -2
- package/packs/dev-team/skills/codebase-tour/evals/files/3/go.mod +2 -2
- package/packs/dev-team/skills/dependency-upgrade-review/SKILL.md +52 -34
- package/packs/dev-team/skills/dependency-upgrade-review/evals/evals.json +33 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/.github/workflows/ci.yml +39 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/.github/workflows/integration.yml +20 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/PR.md +39 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/go.mod +17 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/internal/notify/client.go +35 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/internal/payments/client.go +66 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/internal/payments/client_integration_test.go +38 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/internal/payments/client_test.go +53 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/internal/payments/settle.go +30 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/pr.diff +42 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/6/vendor-notes/httpkit-CHANGELOG.md +43 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/.github/workflows/ci.yml +67 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/.lintwright.json +7 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/.nvmrc +1 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/PR.md +110 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/package.json +25 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/pr.diff +146 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/src/due-date.ts +16 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/src/totals.ts +11 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/tests/due-date.test.ts +17 -0
- package/packs/dev-team/skills/dependency-upgrade-review/evals/files/7/tests/totals.test.ts +12 -0
- package/packs/dev-team/skills/dependency-upgrade-review/examples/ledgerline-tidyparse.md +23 -14
- package/packs/dev-team/skills/dependency-upgrade-review/manifest.json +17 -5
- package/packs/dev-team/skills/dependency-upgrade-review/reference.md +36 -1
- package/packs/dev-team/skills/dependency-upgrade-review/scripts/upgrade_diff.py +22 -4
- package/packs/dev-team/skills/sonar-review/manifest.json +12 -0
- package/packs/infra-as-code/skills/terraform-module/manifest.json +11 -0
- package/packs/infra-as-code/skills/terraform-plan-review/manifest.json +8 -1
- package/packs/solution-architect/skills/figma-diagrams/manifest.json +12 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# dependency-upgrade-review reference
|
|
2
2
|
|
|
3
|
-
Long-form material for the steps in `SKILL.md`: what each ecosystem's diff shows, where the risky signals live, how to list the releases between two versions, how to verify a pin, and how to roll back.
|
|
3
|
+
Long-form material for the steps in `SKILL.md`: what each ecosystem's diff shows, where the risky signals live, how to list the releases between two versions, how to trace a behaviour change to its callers, where runtime floors bite, how to verify a pin, and how to roll back.
|
|
4
4
|
|
|
5
5
|
## Reading a lockfile diff, by ecosystem
|
|
6
6
|
|
|
@@ -44,6 +44,36 @@ What to pull from each release, in this order:
|
|
|
44
44
|
3. **Deprecations:** still works, removed later. A deprecated call the repo makes is a 🟧, not a 🟥.
|
|
45
45
|
4. **Licence:** a changed `license` field or a LICENSE file change. Moves between permissive licences (MIT, BSD, Apache-2.0, ISC) are a note. A move to a copyleft licence (GPL, AGPL, MPL at file level, LGPL) or a source-available licence (BUSL, SSPL, Elastic, "Commons Clause") needs a sign-off from whoever owns licence policy before merge.
|
|
46
46
|
|
|
47
|
+
## Tracing a behaviour change to its callers
|
|
48
|
+
|
|
49
|
+
A search hit is where the review starts. Read the calling function and write what it does under the new version.
|
|
50
|
+
|
|
51
|
+
| Change in the notes | What to look for in the caller | What breaks |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| Returns `None` / `-1` / `false` → raises | `if result is None`, `== -1`, `if not ok` branches; retry loops and fallbacks built on them | The branch never runs; the first failure escapes as an exception and the retry or fallback is dead code |
|
|
54
|
+
| Raises → returns a sentinel | `try` / `except` around the call | The error is swallowed; the code carries on with the sentinel |
|
|
55
|
+
| Exception class renamed or re-parented | `except OldError`, `catch (e instanceof OldError)` | The handler stops catching it |
|
|
56
|
+
| Tuple or dict → object | `a, b = f(...)`, `result["key"]` | Unpacking or key access fails at runtime |
|
|
57
|
+
| Changed default | Every call that does not pass the option | Each one gets the new behaviour silently |
|
|
58
|
+
| Module or function removed | Imports at the top of a file, and imports inside functions | A top-level import fails at startup and in tests; an import inside a function fails only when that function runs |
|
|
59
|
+
|
|
60
|
+
Name the fix in the repo's code, and any migration aid an intermediate release ships (an opt-in flag that turns on the new behaviour early). Moving to that release first lets the code change land and be tested before the major.
|
|
61
|
+
|
|
62
|
+
## Runtime floors
|
|
63
|
+
|
|
64
|
+
A release that drops a runtime version breaks wherever the old runtime still runs, and CI usually runs a newer one.
|
|
65
|
+
|
|
66
|
+
| Floor raised in | Compare with |
|
|
67
|
+
|---|---|
|
|
68
|
+
| Release notes ("requires Python 3.10"), `python_requires`, `python-versions` in `poetry.lock`, `requires-python` in `uv.lock` | Dockerfile `FROM`, `setup-python` version and matrix, `.python-version`, the Python the lockfile was compiled on |
|
|
69
|
+
| `engines.node` in `package-lock.json` or `package.json` | `.nvmrc`, `setup-node` / toolchain version, Dockerfile `FROM`, `engine-strict` in `.npmrc` |
|
|
70
|
+
| `go` directive in `go.mod`, `toolchain` line | `setup-go` version, Dockerfile builder image |
|
|
71
|
+
| `rust-version` (MSRV) in a crate | `rust-toolchain.toml`, CI toolchain |
|
|
72
|
+
| Wheels or prebuilt binaries only for some versions or platforms | Every image and runner that installs it; elsewhere it builds from source and needs a compiler or toolchain |
|
|
73
|
+
| An action's runtime (`node20` → `node24`) | Self-hosted runner versions; GitHub-hosted runners update themselves |
|
|
74
|
+
|
|
75
|
+
A lockfile generated on a newer runtime than production can resolve versions production cannot install. `pip install --require-hashes` and `npm ci` fail at build time, not at review time.
|
|
76
|
+
|
|
47
77
|
## Verifying a pin
|
|
48
78
|
|
|
49
79
|
```bash
|
|
@@ -57,6 +87,7 @@ git ls-remote https://github.com/<owner>/<repo> refs/tags/<tag> 'refs/tags/<tag>
|
|
|
57
87
|
- A SHA with no `# vX.Y.Z` comment: list the tags (`git ls-remote --tags https://github.com/<owner>/<repo>`) and find the SHA. No tag points to it means it is not a release.
|
|
58
88
|
- Tag-only pin (`uses: owner/repo@v3`): the maintainer, or anyone who takes over the account, can point `v3` at new code after review. Pin to the commit the `^{}` line returns and keep the tag as a comment.
|
|
59
89
|
- Branch pin (`@main`): every run gets whatever was pushed last. A PR that introduces one is DO NOT MERGE.
|
|
90
|
+
- Two SHAs for one tag: when the same `owner/repo` and `# vX.Y.Z` comment appear with different SHAs across workflow files, at most one is the tag's commit. Run the command once and compare both; until then, the PR is DO NOT MERGE. Read the PR's commit list: a bot writes every pin the same way, so a pin that differs from the bot's usually came from a hand-made commit.
|
|
60
91
|
|
|
61
92
|
Stakes come from the job, not the action: look at `permissions:` (`contents: write`, `id-token: write`, `packages: write`), the `secrets.*` it receives, and the `environment:` it runs in.
|
|
62
93
|
|
|
@@ -70,6 +101,10 @@ Stakes come from the job, not the action: look at `permissions:` (`contents: wri
|
|
|
70
101
|
| A build or publish step (bundler, packager, uploader) not in the PR workflow | No |
|
|
71
102
|
| A source file no test imports | No |
|
|
72
103
|
| A test that imports the file but never hits the changed behaviour (a default, an edge case the notes name) | Not the part that changed |
|
|
104
|
+
| A test that mocks the dependency with the old contract (a fake that returns a value where the new version raises) | No; it tests the old behaviour |
|
|
105
|
+
| An import inside a function no test calls | No; it never fails at import or collection |
|
|
106
|
+
| An install script, when the PR job installs with `--ignore-scripts` and the release job does not | No; it first runs in the release job, with that job's secrets and permissions |
|
|
107
|
+
| A runtime older than the one CI uses (production image, self-hosted runner) | No |
|
|
73
108
|
|
|
74
109
|
Ways to exercise an unrun path before merge: a `workflow_dispatch` dry run on the PR branch, a release to a test index (TestPyPI, a scratch npm scope), or a test that covers the changed behaviour, added in the same PR.
|
|
75
110
|
|
|
@@ -161,6 +161,7 @@ def host_of(url):
|
|
|
161
161
|
class Report:
|
|
162
162
|
def __init__(self):
|
|
163
163
|
self.changes, self.flags, self.files, self.unattributed = [], [], [], {}
|
|
164
|
+
self.sha_pins = {} # (repo, tag) -> {sha: [files]}, from added `uses:` lines
|
|
164
165
|
|
|
165
166
|
def change(self, path, eco, where, name, old, new, note=None):
|
|
166
167
|
if old == new:
|
|
@@ -256,6 +257,12 @@ def merge_blocks(rep, path, eco, hunks, key_fn, field_res, name_fn, reset_re=Non
|
|
|
256
257
|
rep.flag("source", path, f"{label}: {f} moved from {oh} to {nh}")
|
|
257
258
|
elif nh and not o and f != "index" and nh not in DEFAULT_HOSTS:
|
|
258
259
|
rep.flag("source", path, f"{label}: new package comes from {nh}, not the default registry")
|
|
260
|
+
for f in ("node", "python-versions"):
|
|
261
|
+
orv, nrv = of.get(f, (None,))[0], nf.get(f, (None,))[0]
|
|
262
|
+
if o and nrv and orv != nrv:
|
|
263
|
+
rt = "Node" if f == "node" else "Python"
|
|
264
|
+
rep.flag("runtime", path, f"{label}: requires {rt} {nrv} (was {orv or 'unstated'}); check every "
|
|
265
|
+
f"place it installs and runs (base images, CI versions, runners)")
|
|
259
266
|
ol, nl = of.get("license", (None,))[0], nf.get("license", (None,))[0]
|
|
260
267
|
if ol and nl and ol != nl:
|
|
261
268
|
rep.flag("licence", path, f"{label}: licence changed from {ol} to {nl}")
|
|
@@ -317,7 +324,7 @@ def npm_lock_name(key):
|
|
|
317
324
|
|
|
318
325
|
|
|
319
326
|
NPM_FIELDS = {f: re.compile(rf'^\s*"{f}":\s*"?([^",]+)"?,?\s*$') for f in
|
|
320
|
-
("version", "resolved", "license", "hasInstallScript", "dev", "optional", "gypfile")}
|
|
327
|
+
("version", "resolved", "license", "hasInstallScript", "dev", "optional", "gypfile", "node")}
|
|
321
328
|
|
|
322
329
|
|
|
323
330
|
def h_package_lock(rep, path, hunks):
|
|
@@ -360,7 +367,8 @@ def h_pnpm_lock(rep, path, hunks):
|
|
|
360
367
|
|
|
361
368
|
TOML_PKG_FIELDS = {"version": re.compile(r'^version\s*=\s*"([^"]+)"'),
|
|
362
369
|
"source": re.compile(r'^source\s*=\s*"([^"]+)"'),
|
|
363
|
-
"url": re.compile(r'^(?:url|source\s*=\s*\{\s*(?:registry|git|url))\s*=\s*"([^"]+)"')
|
|
370
|
+
"url": re.compile(r'^(?:url|source\s*=\s*\{\s*(?:registry|git|url))\s*=\s*"([^"]+)"'),
|
|
371
|
+
"python-versions": re.compile(r'^python-versions\s*=\s*"([^"]+)"')}
|
|
364
372
|
|
|
365
373
|
|
|
366
374
|
def h_toml_lock(eco):
|
|
@@ -527,6 +535,7 @@ def h_workflow(rep, path, hunks):
|
|
|
527
535
|
if n and SHA40.fullmatch(n[0]):
|
|
528
536
|
tag_ = re.match(r"^(v?[\w.\-]+)", n[1]).group(1) if n[1] else None
|
|
529
537
|
if tag_:
|
|
538
|
+
rep.sha_pins.setdefault((repo, tag_), {}).setdefault(n[0], []).append(path)
|
|
530
539
|
rep.flag("pin", path, f"{name}@{n[0]} claims {tag_}. UNVERIFIED until the tag resolves to this commit: "
|
|
531
540
|
f"`git ls-remote https://github.com/{repo} refs/tags/{tag_} 'refs/tags/{tag_}^{{}}'` "
|
|
532
541
|
f"(the `^{{}}` line is the commit for an annotated tag; it must equal {n[0]})")
|
|
@@ -591,9 +600,16 @@ def analyse(files, title):
|
|
|
591
600
|
for r in rep.changes:
|
|
592
601
|
short = r["name"].split("/")[-1].lower()
|
|
593
602
|
r["in_title"] = r["name"].lower() in t or (len(short) > 3 and re.search(rf"\b{re.escape(short)}\b", t) is not None)
|
|
603
|
+
for (repo, tag_), shas in rep.sha_pins.items():
|
|
604
|
+
if len(shas) > 1:
|
|
605
|
+
where = "; ".join(f"{s} in {', '.join(dict.fromkeys(ps))}" for s, ps in shas.items())
|
|
606
|
+
rep.flag("pin-conflict", next(iter(shas.values()))[0],
|
|
607
|
+
f"{repo} {tag_} is pinned to {len(shas)} different commits ({where}). A tag points to one "
|
|
608
|
+
f"commit, so at most one of these is {tag_}; find which with "
|
|
609
|
+
f"`git ls-remote https://github.com/{repo} refs/tags/{tag_} 'refs/tags/{tag_}^{{}}'`")
|
|
594
610
|
reasons_stop, reasons_check = [], []
|
|
595
611
|
for fl in rep.flags:
|
|
596
|
-
if fl["kind"] in ("install-script", "source"):
|
|
612
|
+
if fl["kind"] in ("install-script", "source", "pin-conflict"):
|
|
597
613
|
reasons_stop.append(f"{fl['kind']}: {fl['text']}")
|
|
598
614
|
elif fl["kind"] == "pin":
|
|
599
615
|
reasons_check.append("pin: " + fl["text"].split(". ")[0])
|
|
@@ -662,9 +678,11 @@ def render_md(rep, floor, stop, check, title):
|
|
|
662
678
|
out += ["", f"## Beyond the title ({len(beyond)})", ""]
|
|
663
679
|
out += [f"- `{r['name']}` {r['kind']}: {r['old'] or '—'} → {r['new'] or '—'} ({r['file']})" for r in beyond] or ["None."]
|
|
664
680
|
labels = (("install-script", "Install scripts"), ("native", "Native code"), ("source", "Sources and registries"),
|
|
665
|
-
("licence", "Licences"), ("pin", "Pins (verify every SHA; the script cannot)"))
|
|
681
|
+
("licence", "Licences"), ("runtime", "Runtime requirements"), ("pin", "Pins (verify every SHA; the script cannot)"))
|
|
666
682
|
for kind, heading in labels:
|
|
667
683
|
rows = [f for f in rep.flags if f["kind"] == kind]
|
|
684
|
+
if kind == "pin":
|
|
685
|
+
rows = [f for f in rep.flags if f["kind"] == "pin-conflict"] + rows
|
|
668
686
|
out += ["", f"## {heading} ({len(rows)})", ""]
|
|
669
687
|
out += [f"- `{f['file']}`: {f['text']}" for f in rows] or ["None seen in the diff."]
|
|
670
688
|
if rep.unattributed:
|
|
@@ -33,5 +33,17 @@
|
|
|
33
33
|
"model": {
|
|
34
34
|
"tier": "standard",
|
|
35
35
|
"rationale": "Formats Sonar API findings into a report. The hard analysis is Sonar's; this is synthesis. Escalates on very large diffs."
|
|
36
|
+
},
|
|
37
|
+
"permissions": {
|
|
38
|
+
"network": [
|
|
39
|
+
"sonarcloud.io"
|
|
40
|
+
],
|
|
41
|
+
"commands": [
|
|
42
|
+
"sonar-scanner",
|
|
43
|
+
"git",
|
|
44
|
+
"curl"
|
|
45
|
+
],
|
|
46
|
+
"files": "none",
|
|
47
|
+
"notes": "No scripts. Its instructions run sonar-scanner and query the Sonar API with curl, using SONAR_TOKEN from the environment, against sonarcloud.io or the self-hosted SonarQube URL the user configures."
|
|
36
48
|
}
|
|
37
49
|
}
|
|
@@ -37,5 +37,16 @@
|
|
|
37
37
|
"model": {
|
|
38
38
|
"tier": "standard",
|
|
39
39
|
"rationale": "Structured generation against fixed module conventions and a secure-defaults checklist; the judgment is bounded to choosing defaults and validation rules for a described need."
|
|
40
|
+
},
|
|
41
|
+
"permissions": {
|
|
42
|
+
"network": [
|
|
43
|
+
"registry.terraform.io",
|
|
44
|
+
"releases.hashicorp.com"
|
|
45
|
+
],
|
|
46
|
+
"commands": [
|
|
47
|
+
"terraform"
|
|
48
|
+
],
|
|
49
|
+
"files": "none",
|
|
50
|
+
"notes": "No scripts. Its instructions run `terraform fmt`, `terraform init -backend=false` and `terraform validate` on the module it writes. `init` downloads the providers from the Terraform registry; none of the commands contacts a cloud account."
|
|
40
51
|
}
|
|
41
52
|
}
|
|
@@ -40,7 +40,14 @@
|
|
|
40
40
|
"iam",
|
|
41
41
|
"drift"
|
|
42
42
|
],
|
|
43
|
-
"permissions": {
|
|
43
|
+
"permissions": {
|
|
44
|
+
"network": [],
|
|
45
|
+
"commands": [
|
|
46
|
+
"terraform"
|
|
47
|
+
],
|
|
48
|
+
"files": "named-paths",
|
|
49
|
+
"notes": "Reads a plan JSON, which can hold secrets in clear text; the script never prints attribute values. Its instructions run `terraform show -json` to produce that plan JSON."
|
|
50
|
+
},
|
|
44
51
|
"model": {
|
|
45
52
|
"tier": "heavy",
|
|
46
53
|
"rationale": "Adversarial review of a destructive change: judging which replacements lose data, which access changes widen exposure, and what is out of scope needs careful reasoning; a missed destroy costs far more than the model."
|
|
@@ -28,7 +28,18 @@
|
|
|
28
28
|
"design",
|
|
29
29
|
"api"
|
|
30
30
|
],
|
|
31
|
-
"permissions": {
|
|
31
|
+
"permissions": {
|
|
32
|
+
"network": [
|
|
33
|
+
"api.figma.com",
|
|
34
|
+
"pypi.org",
|
|
35
|
+
"files.pythonhosted.org"
|
|
36
|
+
],
|
|
37
|
+
"commands": [
|
|
38
|
+
"pip"
|
|
39
|
+
],
|
|
40
|
+
"files": "named-paths",
|
|
41
|
+
"notes": "Reads FIGMA_TOKEN from the environment to call the Figma REST API. Its instructions install the pinned requirements once with `python3 -m pip install -r requirements.txt`, which reaches PyPI."
|
|
42
|
+
},
|
|
32
43
|
"model": {
|
|
33
44
|
"tier": "standard",
|
|
34
45
|
"rationale": "API calls + spec generation. Bounded transformation."
|