memento-mori-jester 0.1.83 → 0.1.85
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/CHANGELOG.md +12 -0
- package/README.md +5 -1
- package/ROADMAP.md +2 -0
- package/docs/CLI.md +1 -1
- package/docs/DEMO.md +2 -1
- package/docs/FRAMEWORK_TUNING.md +1 -1
- package/docs/GITHUB_ACTIONS.md +15 -0
- package/docs/PRODUCTION_READINESS.md +4 -0
- package/docs/RELEASE.md +3 -1
- package/docs/RELEASE_NOTES_v0.1.84.md +50 -0
- package/docs/RELEASE_NOTES_v0.1.85.md +54 -0
- package/examples/ci/README.md +8 -0
- package/examples/ci/adoption-smoke.yml +42 -0
- package/examples/tuning/README.md +8 -0
- package/package.json +4 -2
- package/scripts/check-ci-adoption.mjs +89 -0
- package/scripts/check-production-readiness.mjs +27 -0
- package/scripts/doctor-framework-tuning.mjs +263 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to Memento Mori Jester are tracked here.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.1.85
|
|
8
|
+
|
|
9
|
+
- Added `examples/ci/adoption-smoke.yml`, a read-only GitHub Actions recipe for checking `doctor`, `summary`, and packaged framework tuning cookbook commands in real repos.
|
|
10
|
+
- Added `npm run ci:adoption:check` to keep the adoption smoke workflow and docs aligned with current commands and safety expectations.
|
|
11
|
+
- Updated README, GitHub Actions docs, CI examples, release docs, production-readiness docs, roadmap, and release notes for the new adoption path.
|
|
12
|
+
|
|
13
|
+
## 0.1.84
|
|
14
|
+
|
|
15
|
+
- Added `npm run framework:tuning:doctor`, a consumer-style smoke check that generates temporary preset configs and runs every cookbook `jester tune <rule-id> --json` command through the built CLI.
|
|
16
|
+
- Wired the tuning doctor into `npm test` and the production-readiness guard.
|
|
17
|
+
- Updated framework tuning docs, cookbook docs, release docs, demo docs, and README guidance for the new doctor check.
|
|
18
|
+
|
|
7
19
|
## 0.1.83
|
|
8
20
|
|
|
9
21
|
- Added `examples/tuning/framework-tuning-cookbook.json`, a small checked cookbook that maps framework-shaped noisy-rule reports to exact `jester tune <rule> --json` commands and fixture IDs.
|
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ npx -y memento-mori-jester@latest github-action --write
|
|
|
68
68
|
|
|
69
69
|
The generated workflow uploads SARIF for code scanning and adds a readable Jester summary to the GitHub Actions run.
|
|
70
70
|
|
|
71
|
+
For a first read-only CI smoke, copy [examples/ci/adoption-smoke.yml](examples/ci/adoption-smoke.yml). It runs `doctor`, `summary --kind command "git reset --hard"`, and the published package's `framework:tuning:doctor` without requiring code-scanning permissions.
|
|
72
|
+
|
|
71
73
|
Expected vibe:
|
|
72
74
|
|
|
73
75
|
```text
|
|
@@ -315,7 +317,7 @@ jester tune coverage --json
|
|
|
315
317
|
|
|
316
318
|
`jester tune coverage` shows the fixture support and confidence signal for every rule, including suggested next actions such as adding coverage, reviewing surprise matches, checking quiet-pass boundaries, or leaving a healthy signal alone.
|
|
317
319
|
|
|
318
|
-
For stack-shaped noise, see [Framework Tuning Examples](docs/FRAMEWORK_TUNING.md). It maps common Next.js, Vite React, FastAPI, Terraform/Kubernetes, security-scan, and AI/MCP false-positive reports to the `jester tune <rule>` command and fixture IDs worth checking first. The checked [framework tuning cookbook](examples/tuning) turns those rows into copy-paste recipes and a machine-readable JSON file.
|
|
320
|
+
For stack-shaped noise, see [Framework Tuning Examples](docs/FRAMEWORK_TUNING.md). It maps common Next.js, Vite React, FastAPI, Terraform/Kubernetes, security-scan, and AI/MCP false-positive reports to the `jester tune <rule>` command and fixture IDs worth checking first. The checked [framework tuning cookbook](examples/tuning) turns those rows into copy-paste recipes and a machine-readable JSON file, and `npm run framework:tuning:doctor` proves those recipes execute through the built CLI.
|
|
319
321
|
|
|
320
322
|
Disable a noisy rule by adding its id to `disabledRules` in `jester.config.json`:
|
|
321
323
|
|
|
@@ -436,6 +438,7 @@ More setup examples:
|
|
|
436
438
|
- [Preset Example Packs](examples/presets)
|
|
437
439
|
- [Review Fixtures](examples/fixtures)
|
|
438
440
|
- [Framework CI Examples](examples/ci)
|
|
441
|
+
- [Adoption Smoke CI](examples/ci/adoption-smoke.yml)
|
|
439
442
|
- [Security Policy](SECURITY.md)
|
|
440
443
|
- [Maintainer Triage](docs/MAINTAINER_TRIAGE.md)
|
|
441
444
|
- [Changelog](CHANGELOG.md)
|
|
@@ -453,6 +456,7 @@ Preset example packs:
|
|
|
453
456
|
|
|
454
457
|
Framework CI examples:
|
|
455
458
|
|
|
459
|
+
- [Adoption Smoke CI](examples/ci/adoption-smoke.yml)
|
|
456
460
|
- [Next.js CI](examples/ci/nextjs.yml)
|
|
457
461
|
- [Vite React CI](examples/ci/vite-react.yml)
|
|
458
462
|
- [Express API CI](examples/ci/express-api.yml)
|
package/ROADMAP.md
CHANGED
|
@@ -6,6 +6,8 @@ Memento Mori Jester is usable today as a CLI, MCP server, GitHub Action, and git
|
|
|
6
6
|
|
|
7
7
|
## Recently Shipped
|
|
8
8
|
|
|
9
|
+
- Adoption smoke CI example in v0.1.85, giving real repos a read-only workflow for `doctor`, `summary`, and packaged framework tuning checks.
|
|
10
|
+
- Framework tuning doctor in v0.1.84, proving cookbook recipes execute through the built CLI with generated preset configs before release.
|
|
9
11
|
- Framework tuning cookbook in v0.1.83, adding checked copy-paste recipes and a machine-readable JSON map for stack-shaped noisy-rule reports.
|
|
10
12
|
- Framework tuning examples and quiet-pass fixture curation in v0.1.82, adding six safe real-world examples plus a guide for framework-shaped noisy-rule reports.
|
|
11
13
|
- Repo-local landing page in v0.1.81, adding a static one-page share surface plus deterministic link checks.
|
package/docs/CLI.md
CHANGED
|
@@ -158,7 +158,7 @@ Use `jester tune <id>` when the question is practical: should this noisy rule be
|
|
|
158
158
|
|
|
159
159
|
When filing a false-positive issue, include redacted `jester summary` output and `jester tune <rule-id> --json` output when possible.
|
|
160
160
|
|
|
161
|
-
For stack-shaped reports, see [Framework Tuning Examples](FRAMEWORK_TUNING.md). It points common Next.js, Vite React, FastAPI, Terraform/Kubernetes, security-scan, and AI/MCP noisy-rule reports at the relevant `jester tune <rule-id>` command and fixture IDs. The checked [framework tuning cookbook](../examples/tuning) keeps copy-paste recipes and fixture IDs in one machine-readable place.
|
|
161
|
+
For stack-shaped reports, see [Framework Tuning Examples](FRAMEWORK_TUNING.md). It points common Next.js, Vite React, FastAPI, Terraform/Kubernetes, security-scan, and AI/MCP noisy-rule reports at the relevant `jester tune <rule-id>` command and fixture IDs. The checked [framework tuning cookbook](../examples/tuning) keeps copy-paste recipes and fixture IDs in one machine-readable place. Maintainers can run `npm run framework:tuning:doctor` to prove every cookbook tune command executes through the built CLI with temporary preset configs.
|
|
162
162
|
|
|
163
163
|
Use `jester tune coverage` when maintaining the rule set. It ranks every rule by fixture support and confidence, shows expected vs unexpected fixture weight, and suggests the next maintenance action for each rule.
|
|
164
164
|
|
package/docs/DEMO.md
CHANGED
|
@@ -382,10 +382,11 @@ The cookbook maps recipe IDs such as `next-vite-public-config`, `terraform-kuber
|
|
|
382
382
|
Maintainers can run:
|
|
383
383
|
|
|
384
384
|
```powershell
|
|
385
|
+
npm.cmd run framework:tuning:doctor
|
|
385
386
|
npm.cmd run framework:tuning:check
|
|
386
387
|
```
|
|
387
388
|
|
|
388
|
-
|
|
389
|
+
The doctor runs every cookbook tune command through the built CLI with temporary preset configs. The check validates [framework-tuning-cookbook.json](../examples/tuning/framework-tuning-cookbook.json) against this guide, the cookbook README, and `examples/fixtures/preset-review-cases.json`.
|
|
389
390
|
|
|
390
391
|
## 15. Framework CI Examples
|
|
391
392
|
|
package/docs/FRAMEWORK_TUNING.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Use this when a rule is noisy in a real project and you want the smallest evidence-backed next step before muting it.
|
|
4
4
|
|
|
5
|
-
For copy-pasteable recipes, see [examples/tuning](../examples/tuning). The machine-readable cookbook is [framework-tuning-cookbook.json](../examples/tuning/framework-tuning-cookbook.json)
|
|
5
|
+
For copy-pasteable recipes, see [examples/tuning](../examples/tuning). The machine-readable cookbook is [framework-tuning-cookbook.json](../examples/tuning/framework-tuning-cookbook.json). `npm run framework:tuning:check` keeps it aligned with this guide and the fixture suite, while `npm run framework:tuning:doctor` runs the cookbook's tune commands through the built CLI with temporary preset configs.
|
|
6
6
|
|
|
7
7
|
Start with the rule that actually fired:
|
|
8
8
|
|
package/docs/GITHUB_ACTIONS.md
CHANGED
|
@@ -20,6 +20,21 @@ The generated workflow reviews pull request diffs, writes `jester.sarif`, upload
|
|
|
20
20
|
|
|
21
21
|
This repository dogfoods that generated workflow in [.github/workflows/memento-mori.yml](../.github/workflows/memento-mori.yml).
|
|
22
22
|
|
|
23
|
+
## Adoption Smoke Workflow
|
|
24
|
+
|
|
25
|
+
Use [examples/ci/adoption-smoke.yml](../examples/ci/adoption-smoke.yml) when you want a first read-only CI check before enabling SARIF/code scanning.
|
|
26
|
+
|
|
27
|
+
It runs:
|
|
28
|
+
|
|
29
|
+
```powershell
|
|
30
|
+
npx -y memento-mori-jester@latest doctor
|
|
31
|
+
npx -y memento-mori-jester@latest summary --kind command "git reset --hard"
|
|
32
|
+
npm run framework:tuning:check
|
|
33
|
+
npm run framework:tuning:doctor
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
The workflow downloads the published npm tarball into a temporary directory before running the framework tuning checks, so it verifies package contents rather than relying on this repository checkout.
|
|
37
|
+
|
|
23
38
|
## Composite Action
|
|
24
39
|
|
|
25
40
|
This repo can be used directly as a GitHub Action:
|
|
@@ -26,6 +26,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
|
|
|
26
26
|
- Action inputs cover `fail-on`, `subject`, `config`, `no-config`, `format`, `output-file`, and `summary`.
|
|
27
27
|
- SARIF output and GitHub step summaries remain separate so users can enable readable summaries without new GitHub write permissions.
|
|
28
28
|
- Example workflows in `examples/` and `examples/ci/` stay aligned with the action shape.
|
|
29
|
+
- `examples/ci/adoption-smoke.yml` gives new repos a read-only smoke workflow for `doctor`, `summary`, and packaged framework tuning checks before code scanning is enabled.
|
|
29
30
|
|
|
30
31
|
## MCP And Agent Setup
|
|
31
32
|
|
|
@@ -59,6 +60,7 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
|
|
|
59
60
|
- `npm run fixtures:check` validates fixture IDs, metadata, unsafe-looking content, duplicate content, and explicit expected/absent rule intent.
|
|
60
61
|
- `npm run fixtures:report` shows fixture coverage by rule, rule family, preset slice, kind, verdict, quiet-pass rule boundaries, and feasible pass-case gaps so maintainers can pick the next fixture target; `npm run fixtures:report -- --markdown` produces a paste-ready maintainer snapshot.
|
|
61
62
|
- `npm run framework:tuning:check` keeps the framework tuning guide, cookbook JSON, cookbook README, and fixture IDs aligned.
|
|
63
|
+
- `npm run framework:tuning:doctor` runs the cookbook tune commands through the built CLI with temporary preset configs, so package consumers do not inherit stale recipes.
|
|
62
64
|
- `npm run promo:card` regenerates the deterministic social preview card, and `npm run promo:check` verifies current repo-local promo assets against the current fixture evidence before maintainers post or refresh the demo.
|
|
63
65
|
- `npm run site:check` verifies the static landing page before maintainers post or host it.
|
|
64
66
|
- npm publish has a manual workflow fallback, but the normal release path is tag-driven trusted publishing.
|
|
@@ -77,6 +79,8 @@ This checklist defines what "production grade" means for Memento Mori Jester rig
|
|
|
77
79
|
- fixture authoring checks are wired into `npm test`.
|
|
78
80
|
- fixture coverage reports are wired into `npm test`.
|
|
79
81
|
- framework tuning cookbook checks are wired into `npm test`.
|
|
82
|
+
- framework tuning cookbook doctor checks are wired into `npm test`.
|
|
83
|
+
- CI adoption example checks are wired into `npm test`.
|
|
80
84
|
- promo freshness checks are wired into `npm test`.
|
|
81
85
|
- site checks are wired into `npm test`.
|
|
82
86
|
|
package/docs/RELEASE.md
CHANGED
|
@@ -13,6 +13,8 @@ npm.cmd run fixtures:report
|
|
|
13
13
|
npm.cmd run fixtures:report -- --json
|
|
14
14
|
npm.cmd run fixtures:report -- --markdown
|
|
15
15
|
npm.cmd run framework:tuning:check
|
|
16
|
+
npm.cmd run framework:tuning:doctor
|
|
17
|
+
npm.cmd run ci:adoption:check
|
|
16
18
|
npm.cmd run promo:card:check
|
|
17
19
|
npm.cmd run promo:check
|
|
18
20
|
npm.cmd run site:check
|
|
@@ -25,7 +27,7 @@ Move the current changelog bullets into a matching version section and add `docs
|
|
|
25
27
|
## 2. Tag And Push
|
|
26
28
|
|
|
27
29
|
```powershell
|
|
28
|
-
git add package.json package-lock.json CHANGELOG.md docs/RELEASE_NOTES_v0.1.x.md docs/PRODUCTION_READINESS.md docs/MAINTAINER_TRIAGE.md docs/FRAMEWORK_TUNING.md examples/tuning scripts/check-framework-tuning.mjs SECURITY.md .github/ISSUE_TEMPLATE
|
|
30
|
+
git add package.json package-lock.json CHANGELOG.md docs/RELEASE_NOTES_v0.1.x.md docs/PRODUCTION_READINESS.md docs/MAINTAINER_TRIAGE.md docs/FRAMEWORK_TUNING.md docs/GITHUB_ACTIONS.md examples/ci examples/tuning scripts/check-ci-adoption.mjs scripts/check-framework-tuning.mjs scripts/doctor-framework-tuning.mjs SECURITY.md .github/ISSUE_TEMPLATE
|
|
29
31
|
git commit -m "Release v0.1.x"
|
|
30
32
|
git tag -a v0.1.x -m "Memento Mori Jester v0.1.x"
|
|
31
33
|
git push origin main
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Memento Mori Jester v0.1.84
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This release adds a consumer-style doctor for the framework tuning cookbook. The cookbook was already checked for doc and fixture alignment; now maintainers can prove the cookbook's tune commands execute through the built CLI with generated preset configs.
|
|
6
|
+
|
|
7
|
+
## What Changed
|
|
8
|
+
|
|
9
|
+
- Added `scripts/doctor-framework-tuning.mjs`.
|
|
10
|
+
- Added `npm run framework:tuning:doctor`.
|
|
11
|
+
- Wired the doctor into `npm test` and production-readiness checks.
|
|
12
|
+
- Updated README, CLI docs, demo docs, release docs, production-readiness docs, roadmap, changelog, and the tuning cookbook docs.
|
|
13
|
+
|
|
14
|
+
## Public Interface
|
|
15
|
+
|
|
16
|
+
- No CLI command changes.
|
|
17
|
+
- No MCP tool changes.
|
|
18
|
+
- No config schema changes.
|
|
19
|
+
- No review rule, scoring, matching, or verdict behavior changes.
|
|
20
|
+
- No GitHub Action or release workflow changes.
|
|
21
|
+
- New maintainer/package script: `npm run framework:tuning:doctor`.
|
|
22
|
+
|
|
23
|
+
## Release Validation
|
|
24
|
+
|
|
25
|
+
```powershell
|
|
26
|
+
npm.cmd test
|
|
27
|
+
npm.cmd run demo:svg:check
|
|
28
|
+
npm.cmd run promo:card:check
|
|
29
|
+
npm.cmd run promo:check
|
|
30
|
+
npm.cmd run framework:tuning:check
|
|
31
|
+
npm.cmd run framework:tuning:doctor
|
|
32
|
+
npm.cmd run fixtures:report
|
|
33
|
+
npm.cmd run fixtures:report -- --json
|
|
34
|
+
npm.cmd run fixtures:report -- --markdown
|
|
35
|
+
npm.cmd run site:check
|
|
36
|
+
npm.cmd run pack:dry
|
|
37
|
+
git diff --check
|
|
38
|
+
node .\dist\cli.js tune coverage --no-config
|
|
39
|
+
git diff | node .\dist\cli.js diff --fail-on block --subject "v0.1.84 framework tuning doctor"
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Expected:
|
|
43
|
+
|
|
44
|
+
- `framework:tuning:check` passes for five recipes,
|
|
45
|
+
- `framework:tuning:doctor` runs 10 executable tune commands,
|
|
46
|
+
- fixture report still shows `Fixtures: 222`,
|
|
47
|
+
- no thin rule coverage,
|
|
48
|
+
- no preset/kind gaps,
|
|
49
|
+
- no rules without quiet-pass coverage,
|
|
50
|
+
- GitHub Release and npm Publish complete from the `v0.1.84` tag.
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Memento Mori Jester v0.1.85
|
|
2
|
+
|
|
3
|
+
## Summary
|
|
4
|
+
|
|
5
|
+
This release adds a narrow adoption smoke workflow for real repos. It gives teams a read-only GitHub Actions recipe that checks the package health, proves a summary review path, and verifies the published framework tuning cookbook scripts before they enable heavier code-scanning workflows.
|
|
6
|
+
|
|
7
|
+
## What Changed
|
|
8
|
+
|
|
9
|
+
- Added `examples/ci/adoption-smoke.yml`.
|
|
10
|
+
- Added `scripts/check-ci-adoption.mjs`.
|
|
11
|
+
- Added `npm run ci:adoption:check`.
|
|
12
|
+
- Wired the adoption checker into `npm test` and production-readiness checks.
|
|
13
|
+
- Updated README, GitHub Actions docs, CI example docs, release docs, production-readiness docs, roadmap, and changelog.
|
|
14
|
+
|
|
15
|
+
## Public Interface
|
|
16
|
+
|
|
17
|
+
- No CLI command changes.
|
|
18
|
+
- No MCP tool changes.
|
|
19
|
+
- No config schema changes.
|
|
20
|
+
- No review rule, scoring, matching, or verdict behavior changes.
|
|
21
|
+
- No GitHub Action input changes.
|
|
22
|
+
- New maintainer/package script: `npm run ci:adoption:check`.
|
|
23
|
+
- New checked example workflow: `examples/ci/adoption-smoke.yml`.
|
|
24
|
+
|
|
25
|
+
## Release Validation
|
|
26
|
+
|
|
27
|
+
```powershell
|
|
28
|
+
npm.cmd test
|
|
29
|
+
npm.cmd run demo:svg:check
|
|
30
|
+
npm.cmd run promo:card:check
|
|
31
|
+
npm.cmd run promo:check
|
|
32
|
+
npm.cmd run framework:tuning:check
|
|
33
|
+
npm.cmd run framework:tuning:doctor
|
|
34
|
+
npm.cmd run ci:adoption:check
|
|
35
|
+
npm.cmd run fixtures:report
|
|
36
|
+
npm.cmd run fixtures:report -- --json
|
|
37
|
+
npm.cmd run fixtures:report -- --markdown
|
|
38
|
+
npm.cmd run site:check
|
|
39
|
+
npm.cmd run pack:dry
|
|
40
|
+
git diff --check
|
|
41
|
+
node .\dist\cli.js summary --kind command "git reset --hard"
|
|
42
|
+
node .\dist\cli.js tune coverage --no-config
|
|
43
|
+
git diff | node .\dist\cli.js diff --fail-on block --subject "v0.1.85 adoption smoke CI"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Expected:
|
|
47
|
+
|
|
48
|
+
- `ci:adoption:check` passes for `examples/ci/adoption-smoke.yml`,
|
|
49
|
+
- `framework:tuning:doctor` still runs 10 executable tune commands,
|
|
50
|
+
- fixture report still shows `Fixtures: 222`,
|
|
51
|
+
- no thin rule coverage,
|
|
52
|
+
- no preset/kind gaps,
|
|
53
|
+
- no rules without quiet-pass coverage,
|
|
54
|
+
- GitHub Release and npm Publish complete from the `v0.1.85` tag.
|
package/examples/ci/README.md
CHANGED
|
@@ -13,8 +13,15 @@ npx -y memento-mori-jester@latest bootstrap --preset web
|
|
|
13
13
|
|
|
14
14
|
Then copy the workflow that best matches your stack into `.github/workflows/memento-mori.yml`.
|
|
15
15
|
|
|
16
|
+
If you want a first CI smoke before enabling code scanning, copy [Adoption Smoke](adoption-smoke.yml). It runs:
|
|
17
|
+
|
|
18
|
+
- `npx -y memento-mori-jester@latest doctor`
|
|
19
|
+
- `npx -y memento-mori-jester@latest summary --kind command "git reset --hard"`
|
|
20
|
+
- `npm run framework:tuning:check` and `npm run framework:tuning:doctor` from the published package tarball
|
|
21
|
+
|
|
16
22
|
## Workflows
|
|
17
23
|
|
|
24
|
+
- [Adoption Smoke](adoption-smoke.yml): read-only setup check for doctor, summary, and framework tuning cookbook commands.
|
|
18
25
|
- [Next.js](nextjs.yml): app-router, middleware, redirects, public env, and browser-rendered UI.
|
|
19
26
|
- [Vite React](vite-react.yml): browser storage, public config, redirects, and unsafe HTML surfaces.
|
|
20
27
|
- [Express API](express-api.yml): CORS, auth bypasses, raw SQL, webhooks, and migrations.
|
|
@@ -25,5 +32,6 @@ Then copy the workflow that best matches your stack into `.github/workflows/meme
|
|
|
25
32
|
## Notes
|
|
26
33
|
|
|
27
34
|
- Workflows upload SARIF and add a readable Actions job summary.
|
|
35
|
+
- The adoption smoke workflow is read-only and does not upload SARIF.
|
|
28
36
|
- Most stacks fail CI only on `BLOCK`.
|
|
29
37
|
- The Terraform/Kubernetes example uses `fail-on: caution` because infra repos often want lower tolerance for risky changes.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
name: Memento Mori Jester Adoption Smoke
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [main]
|
|
6
|
+
workflow_dispatch:
|
|
7
|
+
|
|
8
|
+
permissions:
|
|
9
|
+
contents: read
|
|
10
|
+
|
|
11
|
+
jobs:
|
|
12
|
+
adoption-smoke:
|
|
13
|
+
name: Adoption smoke
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 10
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- name: Checkout
|
|
19
|
+
uses: actions/checkout@v6
|
|
20
|
+
|
|
21
|
+
- name: Setup Node
|
|
22
|
+
uses: actions/setup-node@v6
|
|
23
|
+
with:
|
|
24
|
+
node-version: 24
|
|
25
|
+
|
|
26
|
+
- name: Jester doctor
|
|
27
|
+
run: npx -y memento-mori-jester@latest doctor
|
|
28
|
+
|
|
29
|
+
- name: Command summary smoke
|
|
30
|
+
run: npx -y memento-mori-jester@latest summary --kind command "git reset --hard"
|
|
31
|
+
|
|
32
|
+
- name: Framework tuning cookbook smoke
|
|
33
|
+
shell: bash
|
|
34
|
+
run: |
|
|
35
|
+
set -euo pipefail
|
|
36
|
+
workdir="$(mktemp -d)"
|
|
37
|
+
trap 'rm -rf "$workdir"' EXIT
|
|
38
|
+
npm pack memento-mori-jester@latest --pack-destination "$workdir" --silent
|
|
39
|
+
tar -xzf "$workdir"/memento-mori-jester-*.tgz -C "$workdir"
|
|
40
|
+
npm install --omit=dev --ignore-scripts --prefix "$workdir/package"
|
|
41
|
+
npm run framework:tuning:check --prefix "$workdir/package"
|
|
42
|
+
npm run framework:tuning:doctor --prefix "$workdir/package"
|
|
@@ -4,6 +4,8 @@ These small recipes pair the [Framework Tuning Examples](../../docs/FRAMEWORK_TU
|
|
|
4
4
|
|
|
5
5
|
The machine-readable source is [framework-tuning-cookbook.json](framework-tuning-cookbook.json). It is checked by `npm run framework:tuning:check`, so recipe commands and fixture IDs stay aligned with the public guide and fixture suite.
|
|
6
6
|
|
|
7
|
+
Run `npm run framework:tuning:doctor` after `npm run build` when you want a consumer-style smoke check. It generates temporary preset configs with the built CLI, runs every cookbook `jester tune <rule-id> --json` command, validates the JSON advice shape, and confirms each recipe's fixture IDs are present in the packaged fixture suite.
|
|
8
|
+
|
|
7
9
|
## Recipes
|
|
8
10
|
|
|
9
11
|
| Recipe | Stack | Preset | Tune commands | Fixture examples |
|
|
@@ -21,3 +23,9 @@ The machine-readable source is [framework-tuning-cookbook.json](framework-tuning
|
|
|
21
23
|
3. Compare `fixtureEvidence.quietPassFixtures` and sample fixture descriptions with the local hit.
|
|
22
24
|
4. If the local case is safe but missing from the fixture suite, add a redacted pass fixture before loosening a rule.
|
|
23
25
|
5. If the local case includes a real secret, destructive command, broad permission, skipped eval, or user-controlled execution path, fix the change instead of muting the rule.
|
|
26
|
+
|
|
27
|
+
Maintainer smoke check:
|
|
28
|
+
|
|
29
|
+
```powershell
|
|
30
|
+
npm.cmd run framework:tuning:doctor
|
|
31
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "memento-mori-jester",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.85",
|
|
4
4
|
"description": "A local court-jester sidecar for AI coding agents: review plans, commands, diffs, and final claims before they get too pleased with themselves.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"repository": {
|
|
@@ -40,13 +40,15 @@
|
|
|
40
40
|
"build": "tsc -p tsconfig.json",
|
|
41
41
|
"start": "node dist/server.js",
|
|
42
42
|
"start:mcp": "node dist/server.js",
|
|
43
|
-
"test": "npm run build && node scripts/run-tests.mjs && npm run fixtures:check && npm run fixtures:report && npm run framework:tuning:check && npm run promo:check && npm run site:check && npm run production:check",
|
|
43
|
+
"test": "npm run build && node scripts/run-tests.mjs && npm run fixtures:check && npm run fixtures:report && npm run framework:tuning:check && npm run framework:tuning:doctor && npm run ci:adoption:check && npm run promo:check && npm run site:check && npm run production:check",
|
|
44
44
|
"doctor": "node dist/cli.js doctor",
|
|
45
45
|
"demo:svg": "node scripts/render-demo-svg.mjs",
|
|
46
46
|
"demo:svg:check": "node scripts/render-demo-svg.mjs --check",
|
|
47
47
|
"fixtures:check": "node scripts/check-fixtures.mjs",
|
|
48
48
|
"fixtures:report": "node scripts/report-fixtures.mjs",
|
|
49
49
|
"framework:tuning:check": "node scripts/check-framework-tuning.mjs",
|
|
50
|
+
"framework:tuning:doctor": "node scripts/doctor-framework-tuning.mjs",
|
|
51
|
+
"ci:adoption:check": "node scripts/check-ci-adoption.mjs",
|
|
50
52
|
"promo:card": "node scripts/render-social-card.mjs",
|
|
51
53
|
"promo:card:check": "node scripts/render-social-card.mjs --check",
|
|
52
54
|
"promo:check": "node scripts/check-promo-freshness.mjs",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
|
|
5
|
+
const root = process.cwd();
|
|
6
|
+
const failures = [];
|
|
7
|
+
const workflowPath = "examples/ci/adoption-smoke.yml";
|
|
8
|
+
const workflow = read(workflowPath);
|
|
9
|
+
const examplesReadme = read("examples/ci/README.md");
|
|
10
|
+
const githubActionsDocs = read("docs/GITHUB_ACTIONS.md");
|
|
11
|
+
const readme = read("README.md");
|
|
12
|
+
|
|
13
|
+
const unsafeContentPatterns = [
|
|
14
|
+
{ name: "private key block", pattern: /-----BEGIN [A-Z ]*PRIVATE KEY-----/ },
|
|
15
|
+
{ name: "OpenAI-looking secret key", pattern: /\bsk-(?:proj-)?[A-Za-z0-9_-]{20,}\b/ },
|
|
16
|
+
{ name: "Anthropic-looking secret key", pattern: /\bsk-ant-[A-Za-z0-9_-]{20,}\b/ },
|
|
17
|
+
{ name: "GitHub-looking token", pattern: /\bgh[pousr]_[A-Za-z0-9_]{20,}\b/ },
|
|
18
|
+
{ name: "AWS access key id", pattern: /\bAKIA[0-9A-Z]{16}\b/ },
|
|
19
|
+
{ name: "Slack-looking token", pattern: /\bxox[baprs]-[A-Za-z0-9-]{20,}\b/ },
|
|
20
|
+
{ name: "absolute Unix home path", pattern: /(?:^|[\s"'`])\/(?:Users|home)\/[A-Za-z0-9._-]+/ },
|
|
21
|
+
{ name: "absolute Windows user path", pattern: /[A-Za-z]:\\Users\\[A-Za-z0-9._-]+\\/ }
|
|
22
|
+
];
|
|
23
|
+
|
|
24
|
+
function read(path) {
|
|
25
|
+
return readFileSync(join(root, path), "utf8");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function requireText(path, content, pattern, description) {
|
|
29
|
+
if (!pattern.test(content)) {
|
|
30
|
+
failures.push(`${path} should include ${description}.`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function forbidText(path, content, pattern, description) {
|
|
35
|
+
if (pattern.test(content)) {
|
|
36
|
+
failures.push(`${path} should not include ${description}.`);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
for (const [path, content] of [
|
|
41
|
+
[workflowPath, workflow],
|
|
42
|
+
["examples/ci/README.md", examplesReadme],
|
|
43
|
+
["docs/GITHUB_ACTIONS.md", githubActionsDocs],
|
|
44
|
+
["README.md", readme]
|
|
45
|
+
]) {
|
|
46
|
+
for (const unsafe of unsafeContentPatterns) {
|
|
47
|
+
if (unsafe.pattern.test(content)) {
|
|
48
|
+
failures.push(`${path} appears to contain ${unsafe.name}; adoption examples should stay public and redacted.`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
requireText(workflowPath, workflow, /^name: Memento Mori Jester Adoption Smoke/m, "the adoption smoke workflow name");
|
|
54
|
+
requireText(workflowPath, workflow, /^\s*pull_request:/m, "a pull_request trigger");
|
|
55
|
+
requireText(workflowPath, workflow, /^\s*workflow_dispatch:/m, "a manual trigger");
|
|
56
|
+
requireText(workflowPath, workflow, /^permissions:\s*\n\s*contents: read/m, "read-only contents permission");
|
|
57
|
+
requireText(workflowPath, workflow, /actions\/checkout@v6/, "checkout@v6");
|
|
58
|
+
requireText(workflowPath, workflow, /actions\/setup-node@v6/, "setup-node@v6");
|
|
59
|
+
requireText(workflowPath, workflow, /node-version:\s*24/, "Node 24");
|
|
60
|
+
requireText(workflowPath, workflow, /npx -y memento-mori-jester@latest doctor/, "the doctor command");
|
|
61
|
+
requireText(workflowPath, workflow, /npx -y memento-mori-jester@latest summary --kind command "git reset --hard"/, "the command summary smoke");
|
|
62
|
+
requireText(workflowPath, workflow, /npm pack memento-mori-jester@latest --pack-destination "\$workdir" --silent/, "the registry package smoke");
|
|
63
|
+
requireText(workflowPath, workflow, /npm run framework:tuning:check --prefix "\$workdir\/package"/, "the framework tuning check command");
|
|
64
|
+
requireText(workflowPath, workflow, /npm run framework:tuning:doctor --prefix "\$workdir\/package"/, "the framework tuning doctor command");
|
|
65
|
+
requireText(workflowPath, workflow, /trap 'rm -rf "\$workdir"' EXIT/, "temporary package cleanup");
|
|
66
|
+
forbidText(workflowPath, workflow, /pull_request_target/, "pull_request_target");
|
|
67
|
+
forbidText(workflowPath, workflow, /security-events:\s*write|contents:\s*write|pull-requests:\s*write|id-token:\s*write/, "write permissions");
|
|
68
|
+
forbidText(workflowPath, workflow, /npm publish|gh release|git push/, "release or publish commands");
|
|
69
|
+
|
|
70
|
+
for (const [path, content] of [
|
|
71
|
+
["examples/ci/README.md", examplesReadme],
|
|
72
|
+
["docs/GITHUB_ACTIONS.md", githubActionsDocs],
|
|
73
|
+
["README.md", readme]
|
|
74
|
+
]) {
|
|
75
|
+
requireText(path, content, /adoption-smoke\.yml/, "the adoption smoke workflow link");
|
|
76
|
+
requireText(path, content, /doctor/, "doctor adoption guidance");
|
|
77
|
+
requireText(path, content, /summary --kind command "git reset --hard"/, "summary smoke guidance");
|
|
78
|
+
requireText(path, content, /framework:tuning:doctor/, "framework tuning doctor guidance");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (failures.length > 0) {
|
|
82
|
+
console.error("CI adoption example check failed:");
|
|
83
|
+
for (const failure of failures) {
|
|
84
|
+
console.error(`- ${failure}`);
|
|
85
|
+
}
|
|
86
|
+
process.exit(1);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
console.log("CI adoption example check passed for examples/ci/adoption-smoke.yml.");
|
|
@@ -69,6 +69,8 @@ for (const path of [
|
|
|
69
69
|
"scripts/render-social-card.mjs",
|
|
70
70
|
"scripts/check-site.mjs",
|
|
71
71
|
"scripts/check-framework-tuning.mjs",
|
|
72
|
+
"scripts/doctor-framework-tuning.mjs",
|
|
73
|
+
"scripts/check-ci-adoption.mjs",
|
|
72
74
|
"scripts/check-fixtures.mjs",
|
|
73
75
|
"scripts/report-fixtures.mjs",
|
|
74
76
|
".github/ISSUE_TEMPLATE/bug_report.yml",
|
|
@@ -81,6 +83,7 @@ for (const path of [
|
|
|
81
83
|
"examples/github-action.yml",
|
|
82
84
|
"examples/github-code-scanning.yml",
|
|
83
85
|
"examples/ci/README.md",
|
|
86
|
+
"examples/ci/adoption-smoke.yml",
|
|
84
87
|
"examples/presets/README.md",
|
|
85
88
|
"examples/tuning/README.md",
|
|
86
89
|
"examples/tuning/framework-tuning-cookbook.json",
|
|
@@ -105,6 +108,7 @@ requireText("README.md", /fixtures:report/, "fixture coverage report guidance");
|
|
|
105
108
|
requireText("README.md", /fixtures:report -- --markdown/, "Markdown fixture report guidance");
|
|
106
109
|
requireText("README.md", /FRAMEWORK_TUNING\.md/, "framework tuning guide link");
|
|
107
110
|
requireText("README.md", /examples\/tuning/, "framework tuning cookbook link");
|
|
111
|
+
requireText("README.md", /adoption-smoke\.yml/, "adoption smoke CI link");
|
|
108
112
|
requireText("README.md", /License: PolyForm Noncommercial/, "the noncommercial license badge");
|
|
109
113
|
requireText("docs/PRODUCTION_READINESS.md", /npm package/i, "npm package readiness");
|
|
110
114
|
requireText("docs/PRODUCTION_READINESS.md", /GitHub Action/i, "GitHub Action readiness");
|
|
@@ -119,16 +123,23 @@ requireText("docs/PRODUCTION_READINESS.md", /fixtures:check/, "fixture authoring
|
|
|
119
123
|
requireText("docs/PRODUCTION_READINESS.md", /fixtures:report/, "fixture coverage report readiness");
|
|
120
124
|
requireText("docs/PRODUCTION_READINESS.md", /fixtures:report -- --markdown/, "Markdown fixture report readiness");
|
|
121
125
|
requireText("docs/PRODUCTION_READINESS.md", /framework:tuning:check/, "framework tuning cookbook readiness");
|
|
126
|
+
requireText("docs/PRODUCTION_READINESS.md", /framework:tuning:doctor/, "framework tuning cookbook doctor readiness");
|
|
127
|
+
requireText("docs/PRODUCTION_READINESS.md", /adoption-smoke\.yml/, "adoption smoke CI readiness");
|
|
122
128
|
requireText("docs/PRODUCTION_READINESS.md", /quiet-pass/, "quiet-pass fixture readiness");
|
|
123
129
|
requireText("docs/CLI.md", /jester doctor --json/, "doctor JSON CLI docs");
|
|
124
130
|
requireText("docs/CLI.md", /quiet-pass fixture/, "quiet-pass fixture CLI docs");
|
|
125
131
|
requireText("docs/CLI.md", /FRAMEWORK_TUNING\.md/, "framework tuning CLI link");
|
|
126
132
|
requireText("docs/CLI.md", /examples\/tuning/, "framework tuning cookbook CLI link");
|
|
133
|
+
requireText("docs/CLI.md", /framework:tuning:doctor/, "framework tuning doctor CLI docs");
|
|
127
134
|
requireText("docs/FRAMEWORK_TUNING.md", /Next\.js/, "Next.js framework tuning example");
|
|
128
135
|
requireText("docs/FRAMEWORK_TUNING.md", /FastAPI/, "FastAPI framework tuning example");
|
|
129
136
|
requireText("docs/FRAMEWORK_TUNING.md", /Terraform/, "Terraform framework tuning example");
|
|
130
137
|
requireText("docs/FRAMEWORK_TUNING.md", /jester tune <rule-id> --json/, "framework tuning command guidance");
|
|
131
138
|
requireText("docs/FRAMEWORK_TUNING.md", /framework-tuning-cookbook\.json/, "framework tuning cookbook JSON link");
|
|
139
|
+
requireText("docs/FRAMEWORK_TUNING.md", /framework:tuning:doctor/, "framework tuning doctor guidance");
|
|
140
|
+
requireText("docs/GITHUB_ACTIONS.md", /adoption-smoke\.yml/, "adoption smoke GitHub Actions docs");
|
|
141
|
+
requireText("docs/GITHUB_ACTIONS.md", /summary --kind command "git reset --hard"/, "adoption summary smoke docs");
|
|
142
|
+
requireText("docs/GITHUB_ACTIONS.md", /framework:tuning:doctor/, "adoption framework tuning doctor docs");
|
|
132
143
|
requireText("docs/MAINTAINER_TRIAGE.md", /doctor --json/, "doctor JSON triage prompt");
|
|
133
144
|
requireText("docs/MAINTAINER_TRIAGE.md", /tune <rule-id> --json/, "tune JSON triage prompt");
|
|
134
145
|
requireText("docs/MAINTAINER_TRIAGE.md", /preset-review-cases\.json/, "fixture suite link");
|
|
@@ -139,7 +150,12 @@ requireText("examples/fixtures/README.md", /Adding A Fixture From A Report/, "fi
|
|
|
139
150
|
requireText("examples/fixtures/README.md", /fixtures:check/, "fixture authoring check guidance");
|
|
140
151
|
requireText("examples/fixtures/README.md", /fixtures:report/, "fixture coverage report guidance");
|
|
141
152
|
requireText("examples/fixtures/README.md", /fixtures:report -- --markdown/, "Markdown fixture report guidance");
|
|
153
|
+
requireText("examples/ci/README.md", /adoption-smoke\.yml/, "adoption smoke CI index link");
|
|
154
|
+
requireText("examples/ci/adoption-smoke.yml", /npx -y memento-mori-jester@latest doctor/, "adoption smoke doctor command");
|
|
155
|
+
requireText("examples/ci/adoption-smoke.yml", /summary --kind command "git reset --hard"/, "adoption smoke summary command");
|
|
156
|
+
requireText("examples/ci/adoption-smoke.yml", /framework:tuning:doctor/, "adoption smoke tuning doctor command");
|
|
142
157
|
requireText("examples/tuning/README.md", /framework-tuning-cookbook\.json/, "framework tuning cookbook JSON link");
|
|
158
|
+
requireText("examples/tuning/README.md", /framework:tuning:doctor/, "framework tuning doctor guidance");
|
|
143
159
|
requireText("examples/tuning/README.md", /jester tune <rule-id> --json|jester tune [a-z0-9-]+ --json/, "framework tuning command guidance");
|
|
144
160
|
requireText("examples/tuning/framework-tuning-cookbook.json", /next-vite-public-config/, "Next/Vite tuning recipe");
|
|
145
161
|
requireText("examples/tuning/framework-tuning-cookbook.json", /ai-mcp-tooling/, "AI/MCP tuning recipe");
|
|
@@ -155,9 +171,18 @@ forbidText("scripts/report-fixtures.mjs", /src\/config\.ts|src\/types\.ts/, "sou
|
|
|
155
171
|
requireText("scripts/check-framework-tuning.mjs", /framework-tuning-cookbook\.json/, "framework tuning cookbook check");
|
|
156
172
|
requireText("scripts/check-framework-tuning.mjs", /preset-review-cases\.json/, "framework tuning fixture alignment");
|
|
157
173
|
requireText("scripts/check-framework-tuning.mjs", /unsafeContentPatterns/, "unsafe tuning content checks");
|
|
174
|
+
requireText("scripts/doctor-framework-tuning.mjs", /dist.*cli\.js|cliPath/, "built CLI doctor path");
|
|
175
|
+
requireText("scripts/doctor-framework-tuning.mjs", /config.*init|config", "init"/, "generated preset config doctor");
|
|
176
|
+
requireText("scripts/doctor-framework-tuning.mjs", /tune.*--json|tune", ruleId, "--json"/, "tune JSON doctor command");
|
|
177
|
+
forbidText("scripts/doctor-framework-tuning.mjs", /src\/config\.ts|src\/types\.ts/, "source-only framework tuning doctor dependencies");
|
|
178
|
+
requireText("scripts/check-ci-adoption.mjs", /adoption-smoke\.yml/, "adoption smoke checker target");
|
|
179
|
+
requireText("scripts/check-ci-adoption.mjs", /pull_request_target/, "adoption smoke unsafe trigger guard");
|
|
180
|
+
requireText("scripts/check-ci-adoption.mjs", /framework:tuning:doctor/, "adoption smoke tuning doctor guard");
|
|
158
181
|
requireText("package.json", /"fixtures:check": "node scripts\/check-fixtures\.mjs"/, "fixture authoring check script");
|
|
159
182
|
requireText("package.json", /"fixtures:report": "node scripts\/report-fixtures\.mjs"/, "fixture coverage report script");
|
|
160
183
|
requireText("package.json", /"framework:tuning:check": "node scripts\/check-framework-tuning\.mjs"/, "framework tuning cookbook check script");
|
|
184
|
+
requireText("package.json", /"framework:tuning:doctor": "node scripts\/doctor-framework-tuning\.mjs"/, "framework tuning cookbook doctor script");
|
|
185
|
+
requireText("package.json", /"ci:adoption:check": "node scripts\/check-ci-adoption\.mjs"/, "CI adoption check script");
|
|
161
186
|
requireText("package.json", /"promo:card": "node scripts\/render-social-card\.mjs"/, "social card render script");
|
|
162
187
|
requireText("package.json", /"promo:card:check": "node scripts\/render-social-card\.mjs --check"/, "social card stale check script");
|
|
163
188
|
requireText("package.json", /"promo:check": "node scripts\/check-promo-freshness\.mjs"/, "promo freshness check script");
|
|
@@ -165,6 +190,8 @@ requireText("package.json", /"site:check": "node scripts\/check-site\.mjs"/, "si
|
|
|
165
190
|
requireText("package.json", /npm run fixtures:check/, "fixture authoring check in npm test");
|
|
166
191
|
requireText("package.json", /npm run fixtures:report/, "fixture coverage report in npm test");
|
|
167
192
|
requireText("package.json", /npm run framework:tuning:check/, "framework tuning cookbook check in npm test");
|
|
193
|
+
requireText("package.json", /npm run framework:tuning:doctor/, "framework tuning cookbook doctor in npm test");
|
|
194
|
+
requireText("package.json", /npm run ci:adoption:check/, "CI adoption check in npm test");
|
|
168
195
|
requireText("package.json", /npm run promo:check/, "promo freshness check in npm test");
|
|
169
196
|
requireText("package.json", /npm run site:check/, "site check in npm test");
|
|
170
197
|
requireText("scripts/check-promo-freshness.mjs", /--require-package-version/, "optional strict package-version promo check");
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { dirname, join } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { spawnSync } from "node:child_process";
|
|
7
|
+
|
|
8
|
+
const scriptDir = dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const root = join(scriptDir, "..");
|
|
10
|
+
const cliPath = join(root, "dist", "cli.js");
|
|
11
|
+
const cookbookPath = join(root, "examples", "tuning", "framework-tuning-cookbook.json");
|
|
12
|
+
const fixturesPath = join(root, "examples", "fixtures", "preset-review-cases.json");
|
|
13
|
+
const failures = [];
|
|
14
|
+
const commandPattern = /^jester tune ([a-z0-9]+(?:-[a-z0-9]+)*) --json$/;
|
|
15
|
+
const presets = ["node", "python", "web", "api", "infra", "ai", "security"];
|
|
16
|
+
|
|
17
|
+
function readJson(path) {
|
|
18
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function runCli(args, options = {}) {
|
|
22
|
+
const result = spawnSync(process.execPath, [cliPath, ...args], {
|
|
23
|
+
cwd: root,
|
|
24
|
+
encoding: "utf8",
|
|
25
|
+
maxBuffer: 10 * 1024 * 1024
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
if (result.error) {
|
|
29
|
+
throw result.error;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (result.status !== 0) {
|
|
33
|
+
const detail = [result.stdout, result.stderr].filter(Boolean).join("\n").trim();
|
|
34
|
+
throw new Error(`jester ${args.join(" ")} failed${detail ? `:\n${detail}` : "."}`);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!options.json) {
|
|
38
|
+
return result.stdout;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
return JSON.parse(result.stdout);
|
|
43
|
+
} catch (error) {
|
|
44
|
+
throw new Error(`jester ${args.join(" ")} did not return JSON: ${error instanceof Error ? error.message : String(error)}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (!existsSync(cliPath)) {
|
|
49
|
+
console.error("Framework tuning doctor failed:");
|
|
50
|
+
console.error(`- ${cliPath} is missing. Run npm run build first, or run this from an installed package.`);
|
|
51
|
+
process.exit(1);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
let cookbook;
|
|
55
|
+
let fixtures;
|
|
56
|
+
try {
|
|
57
|
+
cookbook = readJson(cookbookPath);
|
|
58
|
+
fixtures = readJson(fixturesPath);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
console.error(`Could not parse framework tuning doctor inputs: ${error instanceof Error ? error.message : String(error)}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!Array.isArray(cookbook)) {
|
|
65
|
+
console.error(`Framework tuning doctor failed: ${cookbookPath} should contain a JSON array.`);
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (!Array.isArray(fixtures)) {
|
|
70
|
+
console.error(`Framework tuning doctor failed: ${fixturesPath} should contain a JSON array.`);
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const fixtureById = new Map(fixtures.map((fixture) => [fixture.id, fixture]));
|
|
75
|
+
const tmpRoot = mkdtempSync(join(tmpdir(), "jester-framework-tuning-"));
|
|
76
|
+
const configByPreset = new Map();
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
for (const preset of presets) {
|
|
80
|
+
const configPath = join(tmpRoot, `${preset}.config.json`);
|
|
81
|
+
runCli(["config", "init", "--preset", preset, "--path", configPath, "--force"]);
|
|
82
|
+
runCli(["config", "validate", "--config", configPath]);
|
|
83
|
+
configByPreset.set(preset, configPath);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const noConfigCatalog = readRuleCatalog(["rules", "--json", "--no-config"]);
|
|
87
|
+
const presetCatalogs = new Map();
|
|
88
|
+
for (const [preset, configPath] of configByPreset.entries()) {
|
|
89
|
+
presetCatalogs.set(preset, readRuleCatalog(["rules", "--json", "--config", configPath]));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const recipeSummaries = [];
|
|
93
|
+
let commandCount = 0;
|
|
94
|
+
let fixtureReferenceCount = 0;
|
|
95
|
+
|
|
96
|
+
for (const recipe of cookbook) {
|
|
97
|
+
const label = typeof recipe?.id === "string" ? recipe.id : "unknown-recipe";
|
|
98
|
+
const commands = Array.isArray(recipe?.commands) ? recipe.commands : [];
|
|
99
|
+
const fixtureIds = Array.isArray(recipe?.fixtures) ? recipe.fixtures : [];
|
|
100
|
+
const recipeRuleIds = [];
|
|
101
|
+
|
|
102
|
+
for (const command of commands) {
|
|
103
|
+
const match = typeof command === "string" ? command.match(commandPattern) : null;
|
|
104
|
+
if (!match) {
|
|
105
|
+
failures.push(`${label} command should match "jester tune <rule-id> --json". Saw ${String(command)}.`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
recipeRuleIds.push(match[1]);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (const fixtureId of fixtureIds) {
|
|
113
|
+
const fixture = fixtureById.get(fixtureId);
|
|
114
|
+
if (!fixture) {
|
|
115
|
+
failures.push(`${label} references missing fixture ${fixtureId}.`);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const refs = new Set([
|
|
120
|
+
...(Array.isArray(fixture.expectedRuleIds) ? fixture.expectedRuleIds : []),
|
|
121
|
+
...(Array.isArray(fixture.absentRuleIds) ? fixture.absentRuleIds : [])
|
|
122
|
+
]);
|
|
123
|
+
fixtureReferenceCount += refs.size;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
for (const ruleId of recipeRuleIds) {
|
|
127
|
+
const relatedFixtures = fixtureIds.filter((fixtureId) => {
|
|
128
|
+
const fixture = fixtureById.get(fixtureId);
|
|
129
|
+
const refs = new Set([
|
|
130
|
+
...(Array.isArray(fixture?.expectedRuleIds) ? fixture.expectedRuleIds : []),
|
|
131
|
+
...(Array.isArray(fixture?.absentRuleIds) ? fixture.absentRuleIds : [])
|
|
132
|
+
]);
|
|
133
|
+
return refs.has(ruleId);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
if (relatedFixtures.length === 0) {
|
|
137
|
+
failures.push(`${label} command for ${ruleId} should have at least one referenced fixture in expectedRuleIds or absentRuleIds.`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const runContext = chooseRunContext(ruleId, recipe.preset, noConfigCatalog, presetCatalogs, configByPreset);
|
|
141
|
+
if (!runContext) {
|
|
142
|
+
failures.push(`${label} command for ${ruleId} did not resolve in built-in rules or generated preset configs.`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const args = ["tune", ruleId, "--json"];
|
|
147
|
+
if (runContext.configPath) {
|
|
148
|
+
args.push("--config", runContext.configPath);
|
|
149
|
+
} else {
|
|
150
|
+
args.push("--no-config");
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let advice;
|
|
154
|
+
try {
|
|
155
|
+
advice = runCli(args, { json: true });
|
|
156
|
+
} catch (error) {
|
|
157
|
+
failures.push(`${label} command for ${ruleId} failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
158
|
+
continue;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
commandCount += 1;
|
|
162
|
+
validateTuneAdvice(label, ruleId, runContext.name, advice);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
recipeSummaries.push({
|
|
166
|
+
id: label,
|
|
167
|
+
commandCount: recipeRuleIds.length,
|
|
168
|
+
fixtureCount: fixtureIds.length
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (failures.length > 0) {
|
|
173
|
+
console.error("Framework tuning doctor failed:");
|
|
174
|
+
for (const failure of failures) {
|
|
175
|
+
console.error(`- ${failure}`);
|
|
176
|
+
}
|
|
177
|
+
process.exit(1);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
console.log("Framework tuning doctor");
|
|
181
|
+
console.log("");
|
|
182
|
+
for (const recipe of recipeSummaries) {
|
|
183
|
+
console.log(`PASS ${recipe.id}: ${recipe.commandCount} tune command(s), ${recipe.fixtureCount} fixture reference(s)`);
|
|
184
|
+
}
|
|
185
|
+
console.log("");
|
|
186
|
+
console.log(`Checked ${recipeSummaries.length} recipe(s), ${commandCount} executable tune command(s), and ${fixtureReferenceCount} fixture rule reference(s).`);
|
|
187
|
+
console.log("Generated temporary preset configs and removed them after validation.");
|
|
188
|
+
} finally {
|
|
189
|
+
rmSync(tmpRoot, { recursive: true, force: true });
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function readRuleCatalog(args) {
|
|
193
|
+
const output = runCli(args, { json: true });
|
|
194
|
+
if (!Array.isArray(output.rules)) {
|
|
195
|
+
throw new Error(`jester ${args.join(" ")} did not return a rules array.`);
|
|
196
|
+
}
|
|
197
|
+
return new Set(output.rules.map((rule) => rule.id).filter((id) => typeof id === "string"));
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function chooseRunContext(ruleId, preferredPreset, noConfigCatalog, presetCatalogs, configByPreset) {
|
|
201
|
+
if (typeof preferredPreset === "string" && presetCatalogs.get(preferredPreset)?.has(ruleId)) {
|
|
202
|
+
return {
|
|
203
|
+
name: preferredPreset,
|
|
204
|
+
configPath: configByPreset.get(preferredPreset)
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
for (const [preset, catalog] of presetCatalogs.entries()) {
|
|
209
|
+
if (catalog.has(ruleId)) {
|
|
210
|
+
return {
|
|
211
|
+
name: preset,
|
|
212
|
+
configPath: configByPreset.get(preset)
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (noConfigCatalog.has(ruleId)) {
|
|
218
|
+
return {
|
|
219
|
+
name: "built-in",
|
|
220
|
+
configPath: null
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
return null;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function validateTuneAdvice(recipeId, ruleId, contextName, advice) {
|
|
228
|
+
if (advice?.ruleId !== ruleId) {
|
|
229
|
+
failures.push(`${recipeId} expected tune JSON ruleId ${ruleId}; saw ${String(advice?.ruleId)}.`);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
if (typeof advice?.title !== "string" || advice.title.length === 0) {
|
|
233
|
+
failures.push(`${recipeId} tune ${ruleId} should include a title.`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (!Number.isInteger(advice?.severity)) {
|
|
237
|
+
failures.push(`${recipeId} tune ${ruleId} should include numeric severity.`);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if (!Array.isArray(advice?.kinds) || advice.kinds.length === 0) {
|
|
241
|
+
failures.push(`${recipeId} tune ${ruleId} should include review kinds.`);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
if (typeof advice?.recommendation !== "string" || advice.recommendation.length === 0) {
|
|
245
|
+
failures.push(`${recipeId} tune ${ruleId} should include a recommendation.`);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (!Array.isArray(advice?.checksBeforeMuting) || advice.checksBeforeMuting.length === 0) {
|
|
249
|
+
failures.push(`${recipeId} tune ${ruleId} should include checksBeforeMuting.`);
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (advice?.fixtureEvidence?.ruleId !== ruleId) {
|
|
253
|
+
failures.push(`${recipeId} tune ${ruleId} should include fixtureEvidence for the same rule.`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (!advice?.commands?.inspect || !advice?.commands?.validate || !advice?.commands?.list) {
|
|
257
|
+
failures.push(`${recipeId} tune ${ruleId} should include inspect, validate, and list commands.`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
if (contextName !== "built-in" && advice?.configPath === null) {
|
|
261
|
+
failures.push(`${recipeId} tune ${ruleId} should report a generated config path when using the ${contextName} preset.`);
|
|
262
|
+
}
|
|
263
|
+
}
|