@maccesar/aiskills 1.18.1 → 1.20.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/README.md +55 -9
- package/bin/aiskills.js +1 -0
- package/commands/release.md +25 -5
- package/lib/commands/list.js +45 -22
- package/lib/config.js +1 -0
- package/package.json +1 -1
- package/skills/humaniza/SKILL.md +1 -0
- package/skills/npm-supply-chain/SKILL.md +102 -0
- package/skills/npm-supply-chain/assets/publish.yml +51 -0
- package/skills/npm-supply-chain/references/authentication.md +57 -0
- package/skills/npm-supply-chain/references/install-defaults.md +43 -0
- package/skills/npm-supply-chain/references/migration.md +57 -0
- package/skills/npm-supply-chain/references/trusted-publishing.md +79 -0
- package/skills/npm-supply-chain/references/verification.md +84 -0
- package/skills/npm-supply-chain/scripts/auditar_npm.py +509 -0
- package/skills/refactoring-ui/SKILL.md +3 -1
- package/skills/seo-launch/SKILL.md +1 -0
- package/skills/seo-launch/scripts/__pycache__/auditar_seo.cpython-312.pyc +0 -0
- package/skills/stitch-showcase/SKILL.md +1 -0
- package/skills/vscode-extension-dev/SKILL.md +4 -2
package/README.md
CHANGED
|
@@ -74,6 +74,7 @@ All three platforms use the same Agent Skills format: a `SKILL.md` file with YAM
|
|
|
74
74
|
| stitch-showcase | Design Tools | Google Stitch export workflow | 16 files |
|
|
75
75
|
| session-log | Project | Convention + 3 A/B rounds | 2 files |
|
|
76
76
|
| seo-launch | Web / SEO | Head tags, share cards, server files | 5 files |
|
|
77
|
+
| npm-supply-chain | npm / CI | npm and GitHub changelogs, 2025–2026 | 5 files |
|
|
77
78
|
|
|
78
79
|
Use `aiskills list` to see available skills from the command line. Pull requests are welcome.
|
|
79
80
|
|
|
@@ -104,7 +105,7 @@ Example prompts:
|
|
|
104
105
|
```
|
|
105
106
|
|
|
106
107
|
How it works:
|
|
107
|
-
1. **Detect** — reads git status, last tag, existing commits since the tag, version file, `CHANGELOG.md`, `README.md`,
|
|
108
|
+
1. **Detect** — reads git status, last tag, existing commits since the tag, version file, `CHANGELOG.md`, `README.md`, `gh` availability, and `.github/workflows/` to see what a pushed tag will set off. Secondary version files count: a repo carrying `.claude-plugin/plugin.json` gets it bumped to the same number in the same commit, because Claude Code compares that field to invalidate its plugin cache and marketplace users otherwise keep running the old code.
|
|
108
109
|
2. **Group the working tree** — reads each modified/untracked file's diff, infers intent, and groups files into N proposed semantic commits (`feat`, `fix`, `refactor`, `chore`, `docs`, `test`, `build`, `ci`). Excludes screenshots in repo root, scratch files, suspicious binaries — and lists them so you can override.
|
|
109
110
|
3. **Infer bump** — across the union of (existing commits since tag) + (proposed semantic commits): `BREAKING CHANGE` / `!:` → major, any `feat:` → minor, otherwise patch. An argument overrides.
|
|
110
111
|
4. **Compose CHANGELOG** — promotes `[Unreleased]` if present, or generates a Keep-a-Changelog entry from the union of all commits being shipped.
|
|
@@ -493,14 +494,57 @@ How to invoke it — in whatever words you'd use anyway:
|
|
|
493
494
|
|
|
494
495
|
---
|
|
495
496
|
|
|
497
|
+
### npm-supply-chain
|
|
498
|
+
|
|
499
|
+
Both directions of the registry, after everything about them changed between November 2025 and July 2026: how a package gets published, and what happens on the machine installing one.
|
|
500
|
+
|
|
501
|
+
On the publishing side, the long-lived credential is gone. Classic tokens were removed in November 2025 and revoked in December; `npm login` now opens a **two-hour session** rather than writing a token, which is why publishing by hand asks for credentials every time. The remaining token type caps at 90 days for write access, lost account and package management on 31 July 2026, and loses direct publish around January 2027. The one path without an expiry is **trusted publishing**: GitHub Actions identifies itself to npm with a short-lived OIDC credential, so there is no secret to store and npm attaches provenance to the publish automatically.
|
|
502
|
+
|
|
503
|
+
On the installing side, **npm v12** turns off three things that used to happen on their own: dependency lifecycle scripts and implicit node-gyp builds, git dependencies, and remote tarballs. The skill covers what breaks, how `npm approve-scripts` works, and why the resulting allowlist gets committed.
|
|
504
|
+
|
|
505
|
+
`scripts/auditar_npm.py` measures the repo instead of asking about it — standard-library Python, nothing to install, writes nothing:
|
|
506
|
+
|
|
507
|
+
```bash
|
|
508
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py # the repo in the current directory
|
|
509
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py ~/code/foo
|
|
510
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py --no-network
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
It reports tokens sitting in `~/.npmrc` or the project `.npmrc`, Actions secrets that look like npm credentials **and whether any workflow still references them** (one that does not is an orphaned live credential), how each publishing workflow authenticates, `package.json` and `.claude-plugin/plugin.json` versions that have drifted apart, shields.io badges pointing at a package name that does not exist, dependencies that would prompt for approval on npm v12, and the local npm version. Secret *names* are all it reads; a value is never printed.
|
|
514
|
+
|
|
515
|
+
The badge check earns its place from this repo's own history: three badges asked for `aiskills` while the package publishes as `@maccesar/aiskills`, so shields.io rendered "package not found" instead of an error and the downloads badge hid a real 568/month for months.
|
|
516
|
+
|
|
517
|
+
| Reference file | Covers |
|
|
518
|
+
| --- | --- |
|
|
519
|
+
| `authentication.md` | the timeline, session-based login, the three token types, the 2FA-bypass phases with dates, the npmjs.com banner, and which paths remain |
|
|
520
|
+
| `trusted-publishing.md` | the publisher form field by field, `id-token: write`, the Node/npm minimums, provenance, the version guard, and the mistakes that break a registration |
|
|
521
|
+
| `install-defaults.md` | npm v12 defaults, `npm approve-scripts`, the committed allowlist, which packages break, and the `ignore-scripts` trap |
|
|
522
|
+
| `migration.md` | the once-per-project procedure in order, what only the package owner can do, and the cleanup the old flow leaves behind |
|
|
523
|
+
| `verification.md` | the command behind each claim — including why `npm view` reports a stale version and what npmjs.com shows when OIDC worked |
|
|
524
|
+
|
|
525
|
+
`assets/publish.yml` is the workflow template, commented line by line.
|
|
526
|
+
|
|
527
|
+
How to invoke it — in whatever words you'd use anyway:
|
|
528
|
+
|
|
529
|
+
```
|
|
530
|
+
"¿por qué me pide login cada vez que publico?"
|
|
531
|
+
"I want to publish from GitHub Actions instead of my laptop"
|
|
532
|
+
"¿qué es esto del bypass 2FA que me sale en npmjs?"
|
|
533
|
+
"npm install stopped running postinstall after I upgraded"
|
|
534
|
+
"cómo quito el NPM_TOKEN de este repo"
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
---
|
|
538
|
+
|
|
496
539
|
## CLI reference
|
|
497
540
|
|
|
498
541
|
### aiskills list
|
|
499
542
|
|
|
500
|
-
Lists all available skills with their descriptions.
|
|
543
|
+
Lists all available skills with their descriptions, marking each one installed (✓) or not (✗). It works before you have installed anything — descriptions for a skill that is not installed come from the copy bundled in this package — so the list doubles as the catalog of what is on offer.
|
|
501
544
|
|
|
502
545
|
```bash
|
|
503
546
|
aiskills list
|
|
547
|
+
aiskills ls # same thing
|
|
504
548
|
```
|
|
505
549
|
|
|
506
550
|
---
|
|
@@ -688,19 +732,21 @@ Skills are plain Markdown files. To add a new one:
|
|
|
688
732
|
|
|
689
733
|
### Skill frontmatter format
|
|
690
734
|
|
|
735
|
+
Only the six fields the [agentskills.io specification](https://agentskills.io/specification) defines: `name` and `description` are required; `license`, `compatibility`, `metadata` and `allowed-tools` are optional. Anything else is ignored by some agents and rejected by others.
|
|
736
|
+
|
|
691
737
|
```yaml
|
|
692
738
|
---
|
|
693
739
|
name: skill-name
|
|
694
|
-
description:
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
- Bullet list of trigger conditions
|
|
698
|
-
source: "Book title, documentation name, or other authoritative source"
|
|
699
|
-
anti_hallucination_note: >
|
|
700
|
-
What the AI should NOT do (invent, supplement, guess).
|
|
740
|
+
description: 'What this skill does, the words a user would actually say when they need it, and — after "Not for:" — the neighbouring tasks it should not answer.'
|
|
741
|
+
allowed-tools: Read, Grep, Glob, Bash # only if the skill needs more than reading
|
|
742
|
+
compatibility: Requires Python 3 # only if it has real environment requirements
|
|
701
743
|
---
|
|
702
744
|
```
|
|
703
745
|
|
|
746
|
+
The `description` is the only part an assistant reads when deciding whether to load the skill, so every trigger belongs in it. A `## When to use` section in the body cannot affect that decision — it loads after the decision was made — and costs context on every invocation.
|
|
747
|
+
|
|
748
|
+
**The `description` must stay under 1024 characters** and the `name` under 64, both enforced by `test/manifest.test.js`. Past those limits some agents fail to load the skill at all. The block as a whole has no limit, so an optional field costs nothing against the description's budget.
|
|
749
|
+
|
|
704
750
|
### Guidelines
|
|
705
751
|
- Every skill must cite a specific source (book, official docs, specification)
|
|
706
752
|
- No invented numbers, rules, or advice not found in the source
|
package/bin/aiskills.js
CHANGED
package/commands/release.md
CHANGED
|
@@ -77,6 +77,9 @@ Do all of this **internally**. Do not print a status summary or any "Step 1" hea
|
|
|
77
77
|
- `*.podspec` → CocoaPods (edit `s.version`)
|
|
78
78
|
- None → **versionless** mode (commit + push + optional tag).
|
|
79
79
|
|
|
80
|
+
**Then check for secondary version files.** They accompany the primary one, they do not replace it, and they must reach the **same** number in the **same** commit:
|
|
81
|
+
- `.claude-plugin/plugin.json` → a Claude Code plugin manifest. Claude Code compares this `"version"` field to decide whether to invalidate its cached copy of the plugin, so a repo that ships both to npm and to a plugin marketplace can publish new code while marketplace users keep running the old one indefinitely. This is not hypothetical: a project shipped 2.6.0 to npm with `plugin.json` still reading 3.0.0, and the marketplace announced a version that did not exist. If the repo also has a CI job that compares the tag against its version files, an unbumped `plugin.json` fails the release outright.
|
|
82
|
+
|
|
80
83
|
2. **Detect CHANGELOG strategy:** if `CHANGELOG.md` exists and has `## [Unreleased]` → **promote mode**; if exists without it → **generate mode**; if missing → **skip CHANGELOG entirely** (do not create one without asking).
|
|
81
84
|
|
|
82
85
|
3. **Detect README version references:** grep `README.md` for the current version string and `v\d+\.\d+` badge patterns. Note locations that need updating, or note that none exist.
|
|
@@ -143,7 +146,12 @@ Do all of this **internally**. Do not print a status summary or any "Step 1" hea
|
|
|
143
146
|
|
|
144
147
|
In `private` mode these flags are **not** raised — tag and GitHub release are skipped by default anyway, so there is nothing to confirm.
|
|
145
148
|
|
|
146
|
-
9. **
|
|
149
|
+
9. **Detect what the pushed tag will trigger.** Read `.github/workflows/*.yml` (if the directory exists) and look for a workflow whose `on:` block includes `push:` with `tags:` matching `v*` **and** whose steps publish (`npm publish`, `gh release`, `cargo publish`, `twine upload`, or similar).
|
|
150
|
+
|
|
151
|
+
- **A publishing workflow exists** → Phase 3's `git push origin vX.Y.Z` is not just a label, it **starts a publication**. Hold the workflow's filename for Step 4 and Phase 5; the user is entitled to know that confirming ships the package, not just tags it.
|
|
152
|
+
- **The project is npm (a `package.json` with a `name` and no `"private": true`) and no such workflow exists** → hold a one-line recommendation for Step 4. Publishing then still requires an interactive `npm login`, which since December 2025 opens a two-hour session rather than writing a durable token. If the `npm-supply-chain` skill is available in this session, name it as the way to set trusted publishing up. **Do not invoke it and do not create the workflow here** — that is a once-per-project migration that also needs the package owner's 2FA on npmjs.com, and it has no business happening in the middle of a release the user is about to confirm.
|
|
153
|
+
|
|
154
|
+
10. **Note genuine anomalies** for Step 4: no remote, no `gh`, README/CHANGELOG languages disagree, an excluded file is borderline. **Do not** flag a dirty working tree itself — that's expected. **Do not** flag "branch is not main" as an anomaly either — handle it via the dedicated merge prompt below. **Do not** flag "no prior tags" or "no prior releases" as an anomaly on a private repo — in private mode we're not creating either anyway, so it's not a question. On public mode the `first-tag` / `first-release` flags from Step 1.8 are surfaced as their own ⚠️ lines (see Step 4 Part C), not in the generic anomalies list.
|
|
147
155
|
|
|
148
156
|
---
|
|
149
157
|
|
|
@@ -277,8 +285,10 @@ CHANGELOG entry:
|
|
|
277
285
|
- ...
|
|
278
286
|
|
|
279
287
|
README updates (gaps found): <one-line per gap, e.g. "add /release row to Available commands table + new section"> ← omit this line if no gaps
|
|
280
|
-
Release commit: bumps <version-file> A.B.C→X.Y.Z, inserts CHANGELOG section, applies README updates. Subject: `<exact line>`.
|
|
288
|
+
Release commit: bumps <version-file> A.B.C→X.Y.Z (+ <secondary version file> to the same number), inserts CHANGELOG section, applies README updates. Subject: `<exact line>`.
|
|
281
289
|
Push: release commit to <branch>.
|
|
290
|
+
Publishing: pushing the tag triggers `<workflow>.yml`, which publishes to <registry>. ← include ONLY when Step 1.9 found a tag-triggered publishing workflow; localize to user's language
|
|
291
|
+
Sin publicación automática: este repo publica a mano (`npm login` abre una sesión de dos horas). El skill `npm-supply-chain` monta trusted publishing si te interesa — no lo toco aquí. ← include ONLY when Step 1.9 found an npm project with no publishing workflow; localize to user's language
|
|
282
292
|
<one of the three rendering modes below — pick by visibility>
|
|
283
293
|
• Public / internal repo: `Tag + GitHub release: vX.Y.Z to <branch> + release with CHANGELOG notes.`
|
|
284
294
|
• Private repo (default): `Repo privado → omitiendo tag y GitHub release. (responde "con tag" si quieres crear el tag de todos modos)` — localize to user's language.
|
|
@@ -331,7 +341,7 @@ After Phase 1, the working tree should be clean except for files explicitly list
|
|
|
331
341
|
|
|
332
342
|
**Phase 2 — Release commit.**
|
|
333
343
|
|
|
334
|
-
1. **Bump the version file
|
|
344
|
+
1. **Bump the version file** (and every secondary version file found in Step 1.1, to the identical number — `.claude-plugin/plugin.json` with `Edit`):
|
|
335
345
|
- npm: `npm version <patch|minor|major> --no-git-tag-version`
|
|
336
346
|
- tiapp.xml: `Edit` only the **top-level** `<version>` element. Verify no `<module version=...>` matches in your edit.
|
|
337
347
|
- composer.json: `Edit` `"version"` if present; skip if absent.
|
|
@@ -345,7 +355,7 @@ After Phase 1, the working tree should be clean except for files explicitly list
|
|
|
345
355
|
- The documentation gap patches (new command rows, new sections, updated tables) so the README reflects the user-visible surface being shipped in this release.
|
|
346
356
|
- Plus any version badge / install snippet that referenced the old version. Apply with `Edit` per location. Skip only if Step 1.6 found nothing.
|
|
347
357
|
|
|
348
|
-
4. **Stage exactly the release-commit files** (the bumped version file + lockfile if `npm version` updated it + `CHANGELOG.md` + `README.md` if changed). Use explicit `git add <paths>`, not `git add -A`.
|
|
358
|
+
4. **Stage exactly the release-commit files** (the bumped version file + any secondary version file + lockfile if `npm version` updated it + `CHANGELOG.md` + `README.md` if changed). Use explicit `git add <paths>`, not `git add -A`.
|
|
349
359
|
|
|
350
360
|
5. **Commit:** `git commit -m "chore(release): vX.Y.Z"` (or the project's release-commit convention).
|
|
351
361
|
|
|
@@ -381,7 +391,15 @@ If `mode=none` (default), skip this phase entirely.
|
|
|
381
391
|
|
|
382
392
|
If a Phase 4 step fails, the release itself is already shipped (Phases 1–3 succeeded). Surface the merge/PR failure as a non-fatal note in the final report — do not unwind the release.
|
|
383
393
|
|
|
384
|
-
**Phase 5 —
|
|
394
|
+
**Phase 5 — Wait for the publish, then report (one line plus URL).**
|
|
395
|
+
|
|
396
|
+
If Step 1.9 found a tag-triggered publishing workflow and the tag was pushed, the release is not finished when `git push` returns — the workflow is what publishes, and it can still fail on a version guard, on tests, or on a registry credential that was never configured. Wait for it before reporting:
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
gh run watch --exit-status # or: gh run list --workflow=<workflow>.yml --limit 1
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
Report its outcome in the final line. If it failed, say so plainly with the reason and the run URL — the commits and the tag are already landed, so this is information, not something to unwind. Skip this entirely when no such workflow exists or `gh` is unavailable.
|
|
385
403
|
|
|
386
404
|
```
|
|
387
405
|
✓ vX.Y.Z → <release URL>
|
|
@@ -389,6 +407,8 @@ If a Phase 4 step fails, the release itself is already shipped (Phases 1–3 suc
|
|
|
389
407
|
```
|
|
390
408
|
|
|
391
409
|
The "optional note" only appears if something was non-routine — examples:
|
|
410
|
+
- `published by <workflow>.yml` (when the tag triggered a publish and the run went green)
|
|
411
|
+
- `<workflow>.yml failed: <reason> — <run URL>` (the tag and commits are landed; the publish is not)
|
|
392
412
|
- `merged to main; now on <main-branch>` (when Phase 4 ran successfully with mode=merge; include the new main HEAD short hash)
|
|
393
413
|
- `PR opened: <pr-url>` (when Phase 4 ran with mode=pr)
|
|
394
414
|
- `merge to main aborted: main has diverged — resolve manually`
|
package/lib/commands/list.js
CHANGED
|
@@ -111,25 +111,44 @@ export function parseDescription(frontmatter) {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
/**
|
|
114
|
-
* Read a
|
|
115
|
-
* @param {string}
|
|
116
|
-
* @returns {
|
|
114
|
+
* Read a description out of one SKILL.md.
|
|
115
|
+
* @param {string} skillMd - Path to a SKILL.md
|
|
116
|
+
* @returns {string|null} The first sentence, or null when unreadable
|
|
117
117
|
*/
|
|
118
|
-
function
|
|
119
|
-
const skillMd = join(skillDir, 'SKILL.md');
|
|
120
|
-
if (!existsSync(skillMd)) {
|
|
121
|
-
return { description: null, installed: false };
|
|
122
|
-
}
|
|
123
|
-
|
|
118
|
+
function readDescription(skillMd) {
|
|
124
119
|
try {
|
|
125
120
|
const content = readFileSync(skillMd, 'utf8');
|
|
126
121
|
const frontmatter = content.match(/^---\n([\s\S]*?)\n---/);
|
|
127
|
-
if (!frontmatter) return
|
|
128
|
-
|
|
129
|
-
return { description: parseDescription(frontmatter[1]), installed: true };
|
|
122
|
+
if (!frontmatter) return null;
|
|
123
|
+
return parseDescription(frontmatter[1]);
|
|
130
124
|
} catch {
|
|
131
|
-
return
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Read a skill's description, preferring the installed copy and falling back
|
|
131
|
+
* to the one bundled in this package.
|
|
132
|
+
*
|
|
133
|
+
* The fallback is what lets the list describe a skill that is not installed
|
|
134
|
+
* yet — which is exactly when someone runs `aiskills list` and needs to know
|
|
135
|
+
* what they would be installing. `skills/` ships in the npm tarball, so the
|
|
136
|
+
* bundled copy is there for every install channel.
|
|
137
|
+
* @param {string} skillDir - Where the skill would be installed
|
|
138
|
+
* @param {string} name - Skill name, used to find the bundled copy
|
|
139
|
+
* @returns {{description: string|null, installed: boolean}}
|
|
140
|
+
*/
|
|
141
|
+
function readSkillMetadata(skillDir, name) {
|
|
142
|
+
const installedMd = join(skillDir, 'SKILL.md');
|
|
143
|
+
if (existsSync(installedMd)) {
|
|
144
|
+
return { description: readDescription(installedMd), installed: true };
|
|
132
145
|
}
|
|
146
|
+
|
|
147
|
+
const bundledMd = new URL(`../../skills/${name}/SKILL.md`, import.meta.url);
|
|
148
|
+
return {
|
|
149
|
+
description: existsSync(bundledMd) ? readDescription(bundledMd) : null,
|
|
150
|
+
installed: false,
|
|
151
|
+
};
|
|
133
152
|
}
|
|
134
153
|
|
|
135
154
|
export async function listCommand() {
|
|
@@ -139,21 +158,13 @@ export async function listCommand() {
|
|
|
139
158
|
|
|
140
159
|
const skillsDir = getAgentsSkillsDir();
|
|
141
160
|
|
|
142
|
-
if (!existsSync(skillsDir)) {
|
|
143
|
-
console.log(chalk.yellow('No skills installed yet.'));
|
|
144
|
-
console.log('Install with:');
|
|
145
|
-
console.log(chalk.cyan(' aiskills install'));
|
|
146
|
-
console.log('');
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
161
|
const rows = [];
|
|
151
162
|
let installedCount = 0;
|
|
152
163
|
let maxNameLen = 0;
|
|
153
164
|
|
|
154
165
|
for (const name of SKILLS) {
|
|
155
166
|
const skillDir = join(skillsDir, name);
|
|
156
|
-
const { description, installed } = readSkillMetadata(skillDir);
|
|
167
|
+
const { description, installed } = readSkillMetadata(skillDir, name);
|
|
157
168
|
|
|
158
169
|
if (installed) installedCount++;
|
|
159
170
|
if (name.length > maxNameLen) maxNameLen = name.length;
|
|
@@ -179,6 +190,18 @@ export async function listCommand() {
|
|
|
179
190
|
}
|
|
180
191
|
|
|
181
192
|
console.log('');
|
|
193
|
+
|
|
194
|
+
if (installedCount === 0) {
|
|
195
|
+
// Naming the directory would point at somewhere that does not exist yet.
|
|
196
|
+
console.log(chalk.gray(`0/${SKILLS.length} installed`));
|
|
197
|
+
console.log('');
|
|
198
|
+
console.log(chalk.yellow('No skills installed yet.'));
|
|
199
|
+
console.log('Install with:');
|
|
200
|
+
console.log(chalk.cyan(' aiskills install'));
|
|
201
|
+
console.log('');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
|
|
182
205
|
console.log(chalk.gray(`${installedCount}/${SKILLS.length} installed at ${skillsDir}`));
|
|
183
206
|
console.log('');
|
|
184
207
|
console.log(chalk.gray('Run `aiskills status` for installation health.'));
|
package/lib/config.js
CHANGED
package/package.json
CHANGED
package/skills/humaniza/SKILL.md
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name: humaniza
|
|
3
3
|
description: 'Editor de estilo para textos en español (especialmente es-MX): quita los tics de escritura de IA y devuelve prosa natural, concreta y directa, sin cambiar el contenido. Úsalo siempre que alguien quiera revisar, pulir o reescribir un texto en español —emails, documentación, marketing, soporte, posts, textos técnicos— aunque nunca diga "humanizar": "esto suena a ChatGPT", "quítale lo robótico", "hazlo más natural", "que no parezca IA", "sonó muy acartonado", "límale el tono", "revísame este correo antes de mandarlo". También cuando el texto mismo trae las señales: rayas por todas partes, "no es X, sino Y", "cabe destacar", listas de tres, abridores como "la verdad es que". No es para: traducir, corregir solo ortografía o gramática, escribir un texto desde cero, ni editar textos en inglés.'
|
|
4
4
|
allowed-tools: Read, Write, Edit, Grep, Glob, Bash, AskUserQuestion
|
|
5
|
+
compatibility: Requires Python 3 (standard library only) for the pattern checker.
|
|
5
6
|
---
|
|
6
7
|
|
|
7
8
|
# Humaniza
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: npm-supply-chain
|
|
3
|
+
description: 'Publishing to npm and installing from it after the 2025–2026 supply-chain changes: why `npm login` now expires after two hours, classic tokens being gone, granular tokens capped at 90 days and losing direct publish around January 2027, and trusted publishing (OIDC) from GitHub Actions — no stored secret, provenance for free. Also the npm v12 install defaults, where lifecycle scripts, git dependencies and remote tarballs stay off until approved. Use when someone asks why npm demands a login on every publish, wants to publish from CI or GitHub Actions, asks about an NPM_TOKEN, a 2FA-bypass banner on npmjs.com, an OTP prompt, provenance, an `npm install` that broke or stopped running postinstall after an upgrade, or a tag that pushed and never published. Not for: choosing the version number, writing the CHANGELOG, or the release flow itself — that is `/release`.'
|
|
4
|
+
allowed-tools: Read, Grep, Glob, Bash, Edit, Write, AskUserQuestion
|
|
5
|
+
compatibility: Requires Python 3 (standard library only). The audit reads more when git and the gh CLI are available, and degrades cleanly without them.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# npm Supply Chain
|
|
9
|
+
|
|
10
|
+
How a package gets onto npm and what happens when one comes off it. Two halves of the same registry, both rewritten between November 2025 and July 2026: the credentials that used to sit in a file forever are gone, and the install that used to run a stranger's code by default no longer does.
|
|
11
|
+
|
|
12
|
+
Ground every answer in the reference files. This is the area where remembered knowledge is most likely to be a year out of date — the token type you remember may have been revoked, and the flag you remember may now default the other way.
|
|
13
|
+
|
|
14
|
+
Respond in the user's language. This skill is written in English for portability; the report should match whatever language the user is writing in.
|
|
15
|
+
|
|
16
|
+
## Required workflow (read before responding)
|
|
17
|
+
|
|
18
|
+
This SKILL.md is an **index**. The dates, the exact fields, the commands and the failure modes live in the reference files. **Reading this file alone is not enough.**
|
|
19
|
+
|
|
20
|
+
### Step 1 — Open the relevant reference files
|
|
21
|
+
|
|
22
|
+
| Task involves | Required reading |
|
|
23
|
+
|---|---|
|
|
24
|
+
| `npm login` expiring, session-based auth, classic tokens, granular access tokens, the 90-day cap, 2FA-bypass deprecation, the npmjs.com banner, which actions require interactive 2FA | [references/authentication.md](references/authentication.md) |
|
|
25
|
+
| Publishing from GitHub Actions, OIDC, the trusted-publisher form, `id-token: write`, Node/npm minimums, provenance, `NODE_AUTH_TOKEN` | [references/trusted-publishing.md](references/trusted-publishing.md) |
|
|
26
|
+
| npm v12 install behavior, `allowScripts`, `--allow-git`, `--allow-remote`, `npm approve-scripts`, the committed allowlist, packages that break | [references/install-defaults.md](references/install-defaults.md) |
|
|
27
|
+
| Moving a package off token auth: the order of operations, testing the guard, deleting orphaned secrets, cleaning `~/.npmrc` | [references/migration.md](references/migration.md) |
|
|
28
|
+
| Proving any of the above instead of assuming it: `gh api`, `gh secret list`, reading provenance, querying the registry directly, what npmjs.com shows when OIDC worked | [references/verification.md](references/verification.md) |
|
|
29
|
+
|
|
30
|
+
### Step 2 — Output contract
|
|
31
|
+
|
|
32
|
+
Every date, command, field name, version minimum, or registry behavior you cite carries its citation inline:
|
|
33
|
+
|
|
34
|
+
`[source: references/<file>.md]`
|
|
35
|
+
|
|
36
|
+
The citation is what separates a value you read from one that merely sounded right — written down, the two look identical, and the reader has no way to tell them apart. Cite while writing rather than collecting sources at the end, because by then you are reconstructing where something came from instead of recording it.
|
|
37
|
+
|
|
38
|
+
Example: *"Trusted publishing needs Node ≥ 22.14.0 and npm ≥ 11.5.1 on the runner [source: references/trusted-publishing.md]"*
|
|
39
|
+
|
|
40
|
+
### Step 3 — If you must answer from memory
|
|
41
|
+
|
|
42
|
+
If you write a claim without having read the reference that backs it, prepend `FROM_MEMORY (unverified):` to that claim. Do not hide it. Dates in this domain moved three times in eighteen months, and a confidently wrong one sends someone to build a credential that no longer works.
|
|
43
|
+
|
|
44
|
+
### Banned behaviors
|
|
45
|
+
|
|
46
|
+
- **Inventing a date, a version minimum, or a CLI flag that is not in the references.** The whole point of a dated reference is that "npm requires…" was true of a different npm; a plausible answer here costs the user a broken release, not a wrong opinion.
|
|
47
|
+
- **Recommending a long-lived publish token as the default.** It is the shape the registry is actively retiring, and pointing someone at it builds something they have to dismantle within months.
|
|
48
|
+
- **Reading a secret's value, or writing one into a file, a commit or a log.** You may list secret *names* and check whether anything references them; the value is never yours to handle. A token that appears in a transcript is a token that must be revoked.
|
|
49
|
+
- **Changing a package's npm settings on the user's behalf.** Registering a trusted publisher, editing token settings and changing package access all require an interactive 2FA challenge by design — hand over the exact steps and let the user do them.
|
|
50
|
+
- **Marking the answer complete without listing which reference files you read.** The list is what lets the reader tell a grounded answer from a remembered one.
|
|
51
|
+
|
|
52
|
+
## Measure the repo before advising it
|
|
53
|
+
|
|
54
|
+
Run the auditor rather than asking the user what their setup is — most of what matters is readable from disk and from `gh`:
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py # the repo in the current directory
|
|
58
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py ~/code/foo # somewhere else
|
|
59
|
+
python3 <SKILL_DIR>/scripts/auditar_npm.py --no-network
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
Replace `<SKILL_DIR>` with the absolute "Base directory for this skill" from the system message that loaded this skill — the working directory here is the user's project, not this one, so a relative `scripts/…` resolves to nothing. The path also differs by install type (`~/.claude/plugins/cache/<plugin>/<version>/skills/npm-supply-chain` for a plugin install, `~/.agents/skills/npm-supply-chain` standalone), so read it rather than assuming it.
|
|
63
|
+
|
|
64
|
+
It reports: whether `~/.npmrc` and the project `.npmrc` carry a token and whether the registry still accepts it, Actions secrets that look like npm credentials and whether any workflow uses them, which workflows publish and how they authenticate, whether `package.json` and `.claude-plugin/plugin.json` agree on a version, shields.io badges pointing at a package name that does not exist, dependencies with install-time scripts, and the local npm version against v12.
|
|
65
|
+
|
|
66
|
+
It writes nothing. Everything it finds is a proposal for the user to approve, the same separation `seo-launch` keeps between auditing and installing.
|
|
67
|
+
|
|
68
|
+
## The shape of the answer
|
|
69
|
+
|
|
70
|
+
1. **Report what is actually there** — the auditor's output, not a generic checklist.
|
|
71
|
+
2. **Name the deadline that applies**, with its date and source. "This stops working" is only actionable with a when.
|
|
72
|
+
3. **Propose one path**, not a menu. For automated publishing the path is trusted publishing; say so and give the reason rather than laying out three options of which two are dead ends [source: references/authentication.md].
|
|
73
|
+
4. **Split the work by who can do it.** Everything in the repo is yours; everything on npmjs.com needs the user's interactive 2FA [source: references/migration.md].
|
|
74
|
+
5. **Verify afterwards against the registry**, not against the local CLI, and say which command proved it [source: references/verification.md].
|
|
75
|
+
|
|
76
|
+
## Anti-Patterns
|
|
77
|
+
|
|
78
|
+
**Credentials**
|
|
79
|
+
|
|
80
|
+
- Storing a publish token in `~/.npmrc` and treating it as permanent — classic tokens were removed in November 2025 and revoked in December, so the file usually holds a dead string that produces a confusing 401 long before any 2FA prompt [source: references/authentication.md]
|
|
81
|
+
- Reaching for a 2FA-bypass granular token to "stop the login prompts" — it caps at 90 days, it already lost account and package management in July 2026, and it loses direct publish around January 2027 [source: references/authentication.md]
|
|
82
|
+
- Leaving an `NPM_TOKEN` secret in a repo whose workflow no longer uses it. It is a live credential with nothing watching it [source: references/migration.md]
|
|
83
|
+
- Assuming the npmjs.com 2FA-bypass banner is about your account. It is a site-wide campaign notice; the Access Tokens page is what answers whether it applies to you [source: references/authentication.md]
|
|
84
|
+
|
|
85
|
+
**Trusted publishing**
|
|
86
|
+
|
|
87
|
+
- Omitting `permissions: id-token: write`. Without it there is no OIDC token to mint, `npm publish` falls back to token auth, and the failure reads like a credentials problem rather than a missing permission [source: references/trusted-publishing.md]
|
|
88
|
+
- Typing the local folder name into the publisher's `Repository` field. The claim is matched against the repository's canonical name on GitHub, which is not always how the folder is spelled on disk [source: references/trusted-publishing.md]
|
|
89
|
+
- Registering the publisher before the workflow file exists on the default branch, or renaming the workflow afterwards. The registration names the file, so the rename breaks publishing until npmjs.com is updated to match [source: references/trusted-publishing.md]
|
|
90
|
+
- Setting `NODE_AUTH_TOKEN` alongside OIDC "just in case". It puts the publish back on token auth and silently gives up provenance [source: references/trusted-publishing.md]
|
|
91
|
+
- Running the publish job on a Node old enough that the npm shipped with it cannot do OIDC — it degrades to token auth instead of erroring clearly [source: references/trusted-publishing.md]
|
|
92
|
+
|
|
93
|
+
**Installing**
|
|
94
|
+
|
|
95
|
+
- Upgrading to npm v12 and reading the missing `postinstall` as a broken package. The scripts are off by default now; `npm approve-scripts` is what turns the intended ones back on [source: references/install-defaults.md]
|
|
96
|
+
- Approving every script the prompt lists to make CI green. The allowlist is a security decision that gets committed and inherited by the whole team [source: references/install-defaults.md]
|
|
97
|
+
- Assuming a clean local install means a clean CI install when `ignore-scripts=true` sits in a personal `~/.npmrc`. The machine has been running v12 behavior for years and CI has not [source: references/install-defaults.md]
|
|
98
|
+
|
|
99
|
+
**Verifying**
|
|
100
|
+
|
|
101
|
+
- Reporting `npm view <pkg> version` as the state of the registry. The CLI caches it, and it will happily report the previous version minutes after a successful publish [source: references/verification.md]
|
|
102
|
+
- Calling a release published because the tag pushed. The tag starts a workflow; the workflow is what publishes, and it can fail on the version guard, on tests, or on a publisher that was never registered [source: references/verification.md]
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish to npm
|
|
2
|
+
|
|
3
|
+
# Publishes when a vX.Y.Z tag is pushed — which is the last thing a release does.
|
|
4
|
+
# Authentication is trusted publishing (OIDC): no NPM_TOKEN, no secrets, no 2FA prompt.
|
|
5
|
+
# The trusted publisher registered on npmjs.com must point at this exact filename,
|
|
6
|
+
# so renaming this file breaks publishing until the registration is updated.
|
|
7
|
+
|
|
8
|
+
on:
|
|
9
|
+
push:
|
|
10
|
+
tags:
|
|
11
|
+
- 'v*'
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
id-token: write # required for OIDC — without it npm publish falls back to token auth
|
|
16
|
+
|
|
17
|
+
jobs:
|
|
18
|
+
publish:
|
|
19
|
+
runs-on: ubuntu-latest
|
|
20
|
+
steps:
|
|
21
|
+
- uses: actions/checkout@v7
|
|
22
|
+
|
|
23
|
+
- uses: actions/setup-node@v7
|
|
24
|
+
with:
|
|
25
|
+
node-version: '24' # trusted publishing needs Node >= 22.14.0 and npm >= 11.5.1
|
|
26
|
+
registry-url: 'https://registry.npmjs.org'
|
|
27
|
+
# No NODE_AUTH_TOKEN anywhere: setting it puts the publish back on token
|
|
28
|
+
# auth and gives up provenance.
|
|
29
|
+
|
|
30
|
+
# A tag and a manifest can disagree, and npm would publish the manifest's
|
|
31
|
+
# version under a tag that says something else. This fails before npm ci, so
|
|
32
|
+
# a mismatched tag never reaches the registry. Add one line per version file
|
|
33
|
+
# the repo carries — drop the plugin.json block if there is no Claude Code
|
|
34
|
+
# plugin here.
|
|
35
|
+
- name: Verify the tag matches every version file
|
|
36
|
+
run: |
|
|
37
|
+
echo "node $(node --version) / npm $(npm --version)"
|
|
38
|
+
tag="${GITHUB_REF_NAME#v}"
|
|
39
|
+
pkg=$(node -p "require('./package.json').version")
|
|
40
|
+
plugin=$(node -p "require('./.claude-plugin/plugin.json').version")
|
|
41
|
+
echo "tag=$tag package.json=$pkg plugin.json=$plugin"
|
|
42
|
+
[ "$tag" = "$pkg" ] || { echo "::error::tag $tag does not match package.json $pkg"; exit 1; }
|
|
43
|
+
[ "$tag" = "$plugin" ] || { echo "::error::tag $tag does not match plugin.json $plugin"; exit 1; }
|
|
44
|
+
|
|
45
|
+
- run: npm ci
|
|
46
|
+
|
|
47
|
+
- run: npm test
|
|
48
|
+
|
|
49
|
+
# npm attaches provenance automatically when the publish is authenticated by
|
|
50
|
+
# OIDC: the published version links back to this commit and this run.
|
|
51
|
+
- run: npm publish
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Authentication
|
|
2
|
+
|
|
3
|
+
What proves to npm that a publish is yours. Every long-lived option in this space was removed or put on a clock between November 2025 and January 2027, so the answer to "how do I stop typing my password" is different from what it was a year ago.
|
|
4
|
+
|
|
5
|
+
## The timeline
|
|
6
|
+
|
|
7
|
+
| When | What changed |
|
|
8
|
+
|---|---|
|
|
9
|
+
| November 2025 | Classic tokens removed from npm |
|
|
10
|
+
| December 2025 | Classic tokens revoked; `npm login` starts issuing a **two-hour session** instead of a token; CLI token management lands |
|
|
11
|
+
| 8 July 2026 | npm v12 goes GA with the install-time defaults, and the 2FA-bypass GAT deprecation is announced |
|
|
12
|
+
| 31 July 2026 | **Phase 1 applied**: 2FA-bypass tokens lose account, package and organization management |
|
|
13
|
+
| ~January 2027 | **Phase 2 expected**: 2FA-bypass tokens lose the ability to publish directly |
|
|
14
|
+
|
|
15
|
+
Sources: [npm classic tokens revoked, session-based auth and CLI token management](https://github.blog/changelog/2025-12-09-npm-classic-tokens-revoked-session-based-auth-and-cli-token-management-now-available/), [npm install-time security and GAT bypass2fa deprecation](https://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/), [Restricting npm bypass-2FA granular access tokens](https://github.blog/changelog/2026-07-31-restricting-npm-bypass-2fa-granular-access-tokens/).
|
|
16
|
+
|
|
17
|
+
## Why `npm login` keeps coming back
|
|
18
|
+
|
|
19
|
+
Since December 2025 `npm login` does not write a durable token. It opens a **session that lasts two hours**. Publish inside that window or authenticate again. This is not a token expiring early and there is no setting that extends it — it is the mechanism that replaced classic tokens.
|
|
20
|
+
|
|
21
|
+
The common symptom is a `401 Unauthorized` from `npm whoami` on a machine that "was logged in", because `~/.npmrc` still holds an `_authToken` line from the old regime that the registry no longer honors. `npm logout` clears it; the stale line is otherwise harmless and only produces confusing errors.
|
|
22
|
+
|
|
23
|
+
## The three token types, and which are alive
|
|
24
|
+
|
|
25
|
+
- **Classic tokens** — the long-lived, non-expiring strings people pasted into `.npmrc` and forgot. **Removed November 2025, revoked December 2025.** No flag, option or command brings them back.
|
|
26
|
+
- **Granular access tokens (GATs)** — the only token type still issued. Scoped to specific packages or organizations, with an expiry. **Tokens with write access cap at 90 days**, so a publish token has to be rotated four times a year.
|
|
27
|
+
- **GATs configured to bypass 2FA** — the subset built for unattended CI publishing. These are the ones being retired in two phases (below).
|
|
28
|
+
|
|
29
|
+
## The 2FA-bypass retirement, in two phases
|
|
30
|
+
|
|
31
|
+
**Phase 1 — already in effect since 31 July 2026.** A 2FA-bypass GAT can no longer perform sensitive account, package and organization management. Concretely: creating or deleting tokens; generating recovery codes; changing your password, email, profile or 2FA configuration; changing package access, maintainers, or **the trusted publishing configuration**; managing organization and team membership or their package grants. All of those now require an interactive 2FA challenge.
|
|
32
|
+
|
|
33
|
+
**Phase 2 — expected around January 2027.** The same tokens lose the ability to publish directly. Their publishing surface shrinks to reading private packages and *staging* a publish, where the package only becomes public after a human approves it with 2FA.
|
|
34
|
+
|
|
35
|
+
**What this does not touch:** GitHub personal access tokens, GitHub App tokens, and the `GITHUB_TOKEN` that Actions injects. The restriction is specific to npm granular access tokens. This clarification came with the 31 July post and was not in the 8 July announcement.
|
|
36
|
+
|
|
37
|
+
## The banner on npmjs.com
|
|
38
|
+
|
|
39
|
+
The 2FA-bypass notice shown when you log into npmjs.com is a **site-wide campaign banner**, not a diagnosis of the account looking at it. It appears whether or not you own a single bypass token.
|
|
40
|
+
|
|
41
|
+
To answer whether it applies: npmjs.com → avatar → **Access Tokens**, and read the **Bypass 2FA** column. An empty list, or a list where that column is blank on every row, means the deprecation does not touch the account at all. Expired tokens still show in that list and are worth deleting — a token that expired months ago is noise, and noise is what hides the one that matters.
|
|
42
|
+
|
|
43
|
+
## The three remaining paths
|
|
44
|
+
|
|
45
|
+
| Option | Friction per release | Lifetime |
|
|
46
|
+
|---|---|---|
|
|
47
|
+
| **A. Interactive** — `npm login`, then publish | one login (plus an OTP per publish) | indefinite |
|
|
48
|
+
| **B. GAT with 2FA bypass** in `~/.npmrc` | none | **90 days max**, and direct publish dies ~January 2027 |
|
|
49
|
+
| **C. Trusted publishing (OIDC)** from Actions | none | indefinite; the registry's own recommendation |
|
|
50
|
+
|
|
51
|
+
**B is the option the deprecation exists to discourage**: a long-lived secret on disk, renewable four times a year, with a known end date. Building it now means dismantling it within months.
|
|
52
|
+
|
|
53
|
+
**C is the only one that returns zero friction without an expiry.** See [trusted-publishing.md](trusted-publishing.md). A is a perfectly good fallback and stays available forever — if OIDC fails for any reason, `npm login` and a manual publish still work.
|
|
54
|
+
|
|
55
|
+
## Package-level hardening
|
|
56
|
+
|
|
57
|
+
npm can mark a package **"Require two-factor authentication and disallow tokens"**, which rejects any token regardless of its own configuration. On a package published by hand (option A) this is free protection. Its interaction with OIDC publishing is **not established here** — do not enable it on a package that publishes through trusted publishing without confirming the two coexist.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Install defaults in npm v12
|
|
2
|
+
|
|
3
|
+
npm v12 went generally available and became `latest` on 8 July 2026. It turns on the install-time security defaults announced in June: three things that used to happen automatically during `npm install` are now opt-in.
|
|
4
|
+
|
|
5
|
+
Source: [npm install-time security and GAT bypass2fa deprecation](https://github.blog/changelog/2026-07-08-npm-install-time-security-and-gat-bypass2fa-deprecation/).
|
|
6
|
+
|
|
7
|
+
## What changed
|
|
8
|
+
|
|
9
|
+
| Before | npm v12 |
|
|
10
|
+
|---|---|
|
|
11
|
+
| Dependency lifecycle scripts (`preinstall`, `install`, `postinstall`) and implicit node-gyp builds ran automatically | **Do not run** unless explicitly allowed (`allowScripts` defaults to off) |
|
|
12
|
+
| Git dependencies (`github:user/repo`), direct or transitive, resolved | **Not resolved** unless allowed (`--allow-git` defaults to none) |
|
|
13
|
+
| Dependencies from remote URLs (an `https://` tarball) resolved | **Not resolved** unless allowed (`--allow-remote` defaults to none) |
|
|
14
|
+
|
|
15
|
+
The reason is the ecosystem's most exploited vector: a compromised package put its payload in a `postinstall` and ran it on your machine as a side effect of `npm install`. That code is now inert until someone approves it.
|
|
16
|
+
|
|
17
|
+
All three were available behind warnings from **npm 11.16.0**, so a project can be rehearsed against the new behavior before upgrading the major.
|
|
18
|
+
|
|
19
|
+
## The migration flow
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm approve-scripts --allow-scripts-pending
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
It lists the packages asking to run install scripts. Approve the ones you trust, and **commit the resulting allowlist in `package.json`** so the team and CI inherit the same decision rather than each machine answering the prompt differently.
|
|
26
|
+
|
|
27
|
+
Two things about that allowlist: it is a security decision, not a build chore — approving everything the prompt offers to make CI green defeats the change entirely — and it is inherited, so whoever commits it is deciding for everyone.
|
|
28
|
+
|
|
29
|
+
## What actually breaks
|
|
30
|
+
|
|
31
|
+
Nothing in a dependency tree of pure JavaScript. The packages that notice are the ones that **download or compile a binary during install**: `sharp`, `puppeteer`, native CLI wrappers, anything using node-gyp. In a Laravel or Vite project the symptom shows up one step removed — assets that stop compiling after a clean `npm ci`.
|
|
32
|
+
|
|
33
|
+
The way to know without guessing is to run `npm approve-scripts --allow-scripts-pending` in each repo and read the list. A package whose `package.json` declares no `preinstall` / `install` / `postinstall` and pulls no node-gyp dependency has nothing to approve, and its users see no prompt at all.
|
|
34
|
+
|
|
35
|
+
## The trap: a personal `ignore-scripts`
|
|
36
|
+
|
|
37
|
+
A developer with `ignore-scripts=true` in `~/.npmrc` has been living with the main v12 behavior for years and will not notice the upgrade at all. Their CI, running with the default configuration, has not — so "it installs fine on my machine" is not evidence about the pipeline, in either direction.
|
|
38
|
+
|
|
39
|
+
Check both: the personal `~/.npmrc`, the project `.npmrc`, and what the CI job actually runs. Only the last one describes what users and the pipeline experience.
|
|
40
|
+
|
|
41
|
+
## As a package author
|
|
42
|
+
|
|
43
|
+
The question is what your *dependents* see. If none of your dependencies declares an install script, a user on npm v12 installs your package without a single approval prompt. This is worth measuring before publishing rather than after an issue arrives: the auditor in this skill reports it, and so does reading the `scripts` block of every installed `package.json`.
|