@limenet-ch/baseline 2.12.0 → 2.12.2
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 +3 -3
- package/js/dist/bin.js +1 -1
- package/js/dist/checks/has-installed-skills-check.d.ts +11 -0
- package/js/dist/checks/has-installed-skills-check.js +32 -0
- package/js/dist/checks/has-installed-skills-check.js.map +1 -0
- package/js/dist/checks/is-ci-lint-complete-check.d.ts +2 -1
- package/js/dist/checks/is-ci-lint-complete-check.js +5 -1
- package/js/dist/checks/is-ci-lint-complete-check.js.map +1 -1
- package/js/dist/commands/check.js +8 -0
- package/js/dist/commands/check.js.map +1 -1
- package/js/dist/commands/install-skills.d.ts +19 -0
- package/js/dist/commands/install-skills.js +42 -10
- package/js/dist/commands/install-skills.js.map +1 -1
- package/js/dist/project.d.ts +6 -0
- package/js/dist/project.js +20 -1
- package/js/dist/project.js.map +1 -1
- package/js/skills/updating-dependencies/SKILL.md +25 -0
- package/package.json +1 -1
- package/policy/policy.json +7 -1
- package/policy/policy.schema.json +22 -1
package/README.md
CHANGED
|
@@ -83,9 +83,9 @@ npm install --save-dev @limenet-ch/baseline
|
|
|
83
83
|
|
|
84
84
|
```bash
|
|
85
85
|
npx baseline check # report issues
|
|
86
|
-
npx baseline check --fix # apply safe fixes, then report what is left
|
|
86
|
+
npx baseline check --fix # apply safe fixes and install/update the skills, then report what is left
|
|
87
87
|
npx baseline periodic # walk through expired periodic checks
|
|
88
|
-
npx baseline install-skills # copy the packaged skills into .claude/skills/
|
|
88
|
+
npx baseline install-skills # copy the packaged skills into .claude/skills/ without running the checks
|
|
89
89
|
```
|
|
90
90
|
|
|
91
91
|
Wire it into the `ci-lint` npm script so CI and the Claude Stop hook both run it — npm has no
|
|
@@ -124,7 +124,7 @@ State lives in `.baseline.json` at the project root (a JS project has no `config
|
|
|
124
124
|
| `hasCiJobs` | same GitLab CI templates, without the `php` job |
|
|
125
125
|
| `hasTrivyConfig` | identical, canonical config included: its `vendor/**`, `storage/logs/` and `.ddev/` skips are inert in a JS project |
|
|
126
126
|
| `ciSetsNodeVersion` | **npm-only**: the Laravel runner does not register it |
|
|
127
|
-
| `isCiLintComplete` | asserts the JS toolchain in the npm script, not pint/phpstan in a composer script:
|
|
127
|
+
| `isCiLintComplete` | asserts the JS toolchain in the npm script, not pint/phpstan in a composer script: whichever linter is installed (`biome` for `@biomejs/biome`, `eslint` for `eslint`), failing if neither is, plus `tsc` once the project has a `.ts`/`.tsx`/`.mts`/`.cts` file outside `node_modules` and dot-directories |
|
|
128
128
|
| `callsBaseline` | hooks the `ci-lint` npm script, since npm has no `post-update-cmd` |
|
|
129
129
|
| `doesNotExcludeUnknownChecks` | identical, against this runner's smaller registry: a name only the Laravel runner knows is as dead here as one that exists nowhere |
|
|
130
130
|
| `usesReleaseIt` | **inverted**: fails if `@release-it/bumper` is configured, because `package.json` is already release-it's source of truth |
|
package/js/dist/bin.js
CHANGED
|
@@ -12,7 +12,7 @@ Usage:
|
|
|
12
12
|
baseline install-skills [--force] [--cwd <path>]
|
|
13
13
|
|
|
14
14
|
Options:
|
|
15
|
-
--fix Repair what can be repaired, then re-verify
|
|
15
|
+
--fix Repair what can be repaired, install/update the skills, then re-verify
|
|
16
16
|
--verbose Show passing checks too
|
|
17
17
|
--force Overwrite skills that are already installed
|
|
18
18
|
--cwd Project root to inspect (default: the current directory)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CheckResult, FixableCheck } from './check.js';
|
|
2
|
+
/**
|
|
3
|
+
* npm-only: the Laravel runner has no counterpart because Laravel Boost
|
|
4
|
+
* discovers and publishes resources/boost/skills/ itself. A JS project has
|
|
5
|
+
* nothing like Boost, so without this check it silently goes without the skills
|
|
6
|
+
* — or keeps the ones from whichever version first ran install-skills.
|
|
7
|
+
*/
|
|
8
|
+
export declare class HasInstalledSkillsCheck extends FixableCheck {
|
|
9
|
+
static readonly checkName = "hasInstalledSkills";
|
|
10
|
+
fix(dry?: boolean): CheckResult;
|
|
11
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { packagedSkills } from '../commands/install-skills.js';
|
|
2
|
+
import { FixableCheck } from './check.js';
|
|
3
|
+
/**
|
|
4
|
+
* npm-only: the Laravel runner has no counterpart because Laravel Boost
|
|
5
|
+
* discovers and publishes resources/boost/skills/ itself. A JS project has
|
|
6
|
+
* nothing like Boost, so without this check it silently goes without the skills
|
|
7
|
+
* — or keeps the ones from whichever version first ran install-skills.
|
|
8
|
+
*/
|
|
9
|
+
export class HasInstalledSkillsCheck extends FixableCheck {
|
|
10
|
+
static checkName = 'hasInstalledSkills';
|
|
11
|
+
fix(dry = false) {
|
|
12
|
+
let result = 'pass';
|
|
13
|
+
for (const skill of packagedSkills()) {
|
|
14
|
+
const installed = this.project.read(skill.target);
|
|
15
|
+
if (installed === skill.contents) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
// The skills belong to the package, like the canonical .editorconfig:
|
|
19
|
+
// a local edit is drift, and an upgrade overwrites it.
|
|
20
|
+
this.comment(installed === null
|
|
21
|
+
? `Skill missing: Create ${skill.target} by running "npx baseline install-skills"`
|
|
22
|
+
: `Skill outdated: ${skill.target} differs from the version shipped with @limenet-ch/baseline — run "npx baseline install-skills --force"`);
|
|
23
|
+
if (dry) {
|
|
24
|
+
result = 'fail';
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
this.project.write(skill.target, skill.contents);
|
|
28
|
+
}
|
|
29
|
+
return dry ? result : this.fix(true);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=has-installed-skills-check.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"has-installed-skills-check.js","sourceRoot":"","sources":["../../src/checks/has-installed-skills-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAA;AAC9D,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAA;AAE3D;;;;;GAKG;AACH,MAAM,OAAO,uBAAwB,SAAQ,YAAY;IACrD,MAAM,CAAmB,SAAS,GAAG,oBAAoB,CAAA;IAEzD,GAAG,CAAC,GAAG,GAAG,KAAK;QACX,IAAI,MAAM,GAAgB,MAAM,CAAA;QAEhC,KAAK,MAAM,KAAK,IAAI,cAAc,EAAE,EAAE,CAAC;YACnC,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAA;YAEjD,IAAI,SAAS,KAAK,KAAK,CAAC,QAAQ,EAAE,CAAC;gBAC/B,SAAQ;YACZ,CAAC;YAED,sEAAsE;YACtE,uDAAuD;YACvD,IAAI,CAAC,OAAO,CACR,SAAS,KAAK,IAAI;gBACd,CAAC,CAAC,yBAAyB,KAAK,CAAC,MAAM,2CAA2C;gBAClF,CAAC,CAAC,mBAAmB,KAAK,CAAC,MAAM,yGAAyG,CACjJ,CAAA;YAED,IAAI,GAAG,EAAE,CAAC;gBACN,MAAM,GAAG,MAAM,CAAA;gBAEf,SAAQ;YACZ,CAAC;YAED,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAA;QACpD,CAAC;QAED,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;IACxC,CAAC;CACJ"}
|
|
@@ -5,7 +5,8 @@ import { Check, type CheckResult } from './check.js';
|
|
|
5
5
|
* just be installed.
|
|
6
6
|
*
|
|
7
7
|
* Which linter is the project's choice: every one it installs has to run, and it
|
|
8
|
-
* has to install at least one.
|
|
8
|
+
* has to install at least one. The type checker is only required once there is
|
|
9
|
+
* TypeScript for it to check.
|
|
9
10
|
*/
|
|
10
11
|
export declare class IsCiLintCompleteCheck extends Check {
|
|
11
12
|
static readonly checkName = "isCiLintComplete";
|
|
@@ -6,7 +6,8 @@ import { Check } from './check.js';
|
|
|
6
6
|
* just be installed.
|
|
7
7
|
*
|
|
8
8
|
* Which linter is the project's choice: every one it installs has to run, and it
|
|
9
|
-
* has to install at least one.
|
|
9
|
+
* has to install at least one. The type checker is only required once there is
|
|
10
|
+
* TypeScript for it to check.
|
|
10
11
|
*/
|
|
11
12
|
export class IsCiLintCompleteCheck extends Check {
|
|
12
13
|
static checkName = 'isCiLintComplete';
|
|
@@ -28,6 +29,9 @@ export class IsCiLintCompleteCheck extends Check {
|
|
|
28
29
|
...installed.map((name) => linters[name]),
|
|
29
30
|
...policy().strings('ciLint.required.js'),
|
|
30
31
|
];
|
|
32
|
+
if (this.project.containsFileWithExtension(policy().strings('ciLint.typeChecker.js.extensions'))) {
|
|
33
|
+
required.push(policy().string('ciLint.typeChecker.js.command'));
|
|
34
|
+
}
|
|
31
35
|
for (const command of required) {
|
|
32
36
|
if (!script.includes(command)) {
|
|
33
37
|
this.comment(`Incomplete ci-lint script in package.json: it must run "${command}"`);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"is-ci-lint-complete-check.js","sourceRoot":"","sources":["../../src/checks/is-ci-lint-complete-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAA;AAQpD
|
|
1
|
+
{"version":3,"file":"is-ci-lint-complete-check.js","sourceRoot":"","sources":["../../src/checks/is-ci-lint-complete-check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AACrC,OAAO,EAAE,KAAK,EAAoB,MAAM,YAAY,CAAA;AAQpD;;;;;;;;GAQG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAC5C,MAAM,CAAmB,SAAS,GAAG,kBAAkB,CAAA;IAEvD,KAAK;QACD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAc,cAAc,CAAC,CAAA;QACtE,MAAM,MAAM,GAAG,WAAW,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,CAAA;QAEhD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;YACvB,IAAI,CAAC,OAAO,CAAC,0EAA0E,CAAC,CAAA;YAExF,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAA;QACvD,MAAM,SAAS,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,CACzC,CAAC,IAAI,EAAE,EAAE,CACL,WAAW,EAAE,YAAY,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS;YAC/C,WAAW,EAAE,eAAe,EAAE,CAAC,IAAI,CAAC,KAAK,SAAS,CACzD,CAAA;QAED,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACzB,IAAI,CAAC,OAAO,CACR,6CAA6C,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,mCAAmC,CAClH,CAAA;YAED,OAAO,MAAM,CAAA;QACjB,CAAC;QAED,MAAM,QAAQ,GAAG;YACb,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAW,CAAC;YACnD,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,oBAAoB,CAAC;SAC5C,CAAA;QAED,IAAI,IAAI,CAAC,OAAO,CAAC,yBAAyB,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,kCAAkC,CAAC,CAAC,EAAE,CAAC;YAC/F,QAAQ,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,CAAC,+BAA+B,CAAC,CAAC,CAAA;QACnE,CAAC;QAED,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;gBAC5B,IAAI,CAAC,OAAO,CAAC,2DAA2D,OAAO,GAAG,CAAC,CAAA;gBAEnF,OAAO,MAAM,CAAA;YACjB,CAAC;QACL,CAAC;QAED,OAAO,MAAM,CAAA;IACjB,CAAC;CACJ"}
|
|
@@ -2,11 +2,19 @@ import { CommentCollector, icon, isError, isFixable } from '../checks/check.js';
|
|
|
2
2
|
import { isPeriodic } from '../checks/periodic-check.js';
|
|
3
3
|
import { createChecks } from '../checks/registry.js';
|
|
4
4
|
import { excludedChecks } from '../state.js';
|
|
5
|
+
import { syncSkills } from './install-skills.js';
|
|
5
6
|
/** Runs every registered check and returns the process exit code. */
|
|
6
7
|
export function runCheck(project, options) {
|
|
7
8
|
const comments = new CommentCollector();
|
|
8
9
|
const excluded = excludedChecks(project);
|
|
9
10
|
let errors = 0;
|
|
11
|
+
// Not a check: there is nothing to decide, only files to keep current. Laravel
|
|
12
|
+
// Boost does the same for the PHP runner on boost:update.
|
|
13
|
+
if (options.fix) {
|
|
14
|
+
for (const target of syncSkills(project)) {
|
|
15
|
+
console.log(`🔧 Skill installed: ${target}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
10
18
|
for (const check of createChecks(project, comments)) {
|
|
11
19
|
// A periodic check that does not apply yields no result at all, matching
|
|
12
20
|
// the PHP runner rather than counting as a pass.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,gBAAgB,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACjG,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAEpD,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"check.js","sourceRoot":"","sources":["../../src/commands/check.ts"],"names":[],"mappings":"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"}
|
|
@@ -10,6 +10,25 @@ import type { Project } from '../project.js';
|
|
|
10
10
|
* vitest and in the published package.
|
|
11
11
|
*/
|
|
12
12
|
export declare function skillsDirectory(): string;
|
|
13
|
+
export interface PackagedSkill {
|
|
14
|
+
name: string;
|
|
15
|
+
target: string;
|
|
16
|
+
contents: string;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Every skill this package ships, with the path it is installed to. Shared by
|
|
20
|
+
* the install-skills command and hasInstalledSkills, so the two can never
|
|
21
|
+
* disagree about what "installed" means.
|
|
22
|
+
*/
|
|
23
|
+
export declare function packagedSkills(): PackagedSkill[];
|
|
24
|
+
/**
|
|
25
|
+
* Brings .claude/skills/ in line with the packaged skills and returns the paths
|
|
26
|
+
* it wrote. Run by `check --fix`, so upgrading the package and fixing is enough
|
|
27
|
+
* to pick up new and changed skills. The skills belong to the package, like the
|
|
28
|
+
* canonical .editorconfig: a local edit is drift and gets overwritten. Skills the
|
|
29
|
+
* package does not ship are left alone.
|
|
30
|
+
*/
|
|
31
|
+
export declare function syncSkills(project: Project): string[];
|
|
13
32
|
export interface InstallSkillsOptions {
|
|
14
33
|
force: boolean;
|
|
15
34
|
}
|
|
@@ -14,6 +14,43 @@ import { fileURLToPath } from 'node:url';
|
|
|
14
14
|
export function skillsDirectory() {
|
|
15
15
|
return join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'skills');
|
|
16
16
|
}
|
|
17
|
+
/**
|
|
18
|
+
* Every skill this package ships, with the path it is installed to. Shared by
|
|
19
|
+
* the install-skills command and hasInstalledSkills, so the two can never
|
|
20
|
+
* disagree about what "installed" means.
|
|
21
|
+
*/
|
|
22
|
+
export function packagedSkills() {
|
|
23
|
+
const source = skillsDirectory();
|
|
24
|
+
if (!existsSync(source)) {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
return readdirSync(source)
|
|
28
|
+
.sort()
|
|
29
|
+
.filter((name) => existsSync(join(source, name, 'SKILL.md')))
|
|
30
|
+
.map((name) => ({
|
|
31
|
+
name,
|
|
32
|
+
target: `.claude/skills/${name}/SKILL.md`,
|
|
33
|
+
contents: readFileSync(join(source, name, 'SKILL.md'), 'utf8'),
|
|
34
|
+
}));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Brings .claude/skills/ in line with the packaged skills and returns the paths
|
|
38
|
+
* it wrote. Run by `check --fix`, so upgrading the package and fixing is enough
|
|
39
|
+
* to pick up new and changed skills. The skills belong to the package, like the
|
|
40
|
+
* canonical .editorconfig: a local edit is drift and gets overwritten. Skills the
|
|
41
|
+
* package does not ship are left alone.
|
|
42
|
+
*/
|
|
43
|
+
export function syncSkills(project) {
|
|
44
|
+
const written = [];
|
|
45
|
+
for (const skill of packagedSkills()) {
|
|
46
|
+
if (project.read(skill.target) === skill.contents) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
project.write(skill.target, skill.contents);
|
|
50
|
+
written.push(skill.target);
|
|
51
|
+
}
|
|
52
|
+
return written;
|
|
53
|
+
}
|
|
17
54
|
export function runInstallSkills(project, options) {
|
|
18
55
|
const source = skillsDirectory();
|
|
19
56
|
if (!existsSync(source)) {
|
|
@@ -22,19 +59,14 @@ export function runInstallSkills(project, options) {
|
|
|
22
59
|
}
|
|
23
60
|
let installed = 0;
|
|
24
61
|
let skipped = 0;
|
|
25
|
-
for (const skill of
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
const target = `.claude/skills/${skill}/SKILL.md`;
|
|
31
|
-
if (project.exists(target) && !options.force) {
|
|
32
|
-
console.log(`⏭ ${skill} (already installed — pass --force to overwrite)`);
|
|
62
|
+
for (const skill of packagedSkills()) {
|
|
63
|
+
if (project.exists(skill.target) && !options.force) {
|
|
64
|
+
console.log(`⏭ ${skill.name} (already installed — pass --force to overwrite)`);
|
|
33
65
|
skipped += 1;
|
|
34
66
|
continue;
|
|
35
67
|
}
|
|
36
|
-
project.write(target,
|
|
37
|
-
console.log(`✅ ${skill} → ${target}`);
|
|
68
|
+
project.write(skill.target, skill.contents);
|
|
69
|
+
console.log(`✅ ${skill.name} → ${skill.target}`);
|
|
38
70
|
installed += 1;
|
|
39
71
|
}
|
|
40
72
|
if (installed === 0 && skipped === 0) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC/D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAA;AAGxC;;;;;;;;;GASG;AACH,MAAM,UAAU,eAAe;IAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAA;AAC9E,CAAC;
|
|
1
|
+
{"version":3,"file":"install-skills.js","sourceRoot":"","sources":["../../src/commands/install-skills.ts"],"names":[],"mappings":"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"}
|
package/js/dist/project.d.ts
CHANGED
|
@@ -23,4 +23,10 @@ export declare class Project {
|
|
|
23
23
|
*/
|
|
24
24
|
writeJson(relative: string, data: unknown): void;
|
|
25
25
|
remove(relative: string): void;
|
|
26
|
+
/**
|
|
27
|
+
* Whether any file in the project ends in one of the extensions. node_modules
|
|
28
|
+
* and dot-directories (.git, .claude, .ddev, …) are not the project's own
|
|
29
|
+
* source, and symlinks are not followed; the walk stops at the first match.
|
|
30
|
+
*/
|
|
31
|
+
containsFileWithExtension(extensions: string[], directory?: string): boolean;
|
|
26
32
|
}
|
package/js/dist/project.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { existsSync, mkdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
/**
|
|
4
4
|
* The project under inspection. Every file access a check makes goes through
|
|
@@ -60,5 +60,24 @@ export class Project {
|
|
|
60
60
|
remove(relative) {
|
|
61
61
|
rmSync(this.path(relative), { recursive: true, force: true });
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Whether any file in the project ends in one of the extensions. node_modules
|
|
65
|
+
* and dot-directories (.git, .claude, .ddev, …) are not the project's own
|
|
66
|
+
* source, and symlinks are not followed; the walk stops at the first match.
|
|
67
|
+
*/
|
|
68
|
+
containsFileWithExtension(extensions, directory = '') {
|
|
69
|
+
for (const entry of readdirSync(this.path(directory), { withFileTypes: true })) {
|
|
70
|
+
if (entry.isFile() && extensions.some((extension) => entry.name.endsWith(extension))) {
|
|
71
|
+
return true;
|
|
72
|
+
}
|
|
73
|
+
if (entry.isDirectory() &&
|
|
74
|
+
entry.name !== 'node_modules' &&
|
|
75
|
+
!entry.name.startsWith('.') &&
|
|
76
|
+
this.containsFileWithExtension(extensions, join(directory, entry.name))) {
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
63
82
|
}
|
|
64
83
|
//# sourceMappingURL=project.js.map
|
package/js/dist/project.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../src/project.ts"],"names":[],"mappings":"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"}
|
|
@@ -75,6 +75,31 @@ Run the lint suite and fix every issue before considering the update complete:
|
|
|
75
75
|
npm run ci-lint
|
|
76
76
|
```
|
|
77
77
|
|
|
78
|
+
### 6. Record the run (last)
|
|
79
|
+
|
|
80
|
+
Once `ci-lint` passes, mark the `updatesDependencies` periodic check as done so it stops prompting
|
|
81
|
+
for the next 30 days. `baseline periodic` asks for confirmation interactively, so write the
|
|
82
|
+
timestamp into `.baseline.json` at the project root directly instead. Get the current time in the
|
|
83
|
+
format the runner writes:
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
node -e "console.log(new Date().toISOString())"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Set it as `periodic.updatesDependencies`, creating the `periodic` object (or the file) if it does
|
|
90
|
+
not exist yet, and leave every other key untouched:
|
|
91
|
+
|
|
92
|
+
```json
|
|
93
|
+
{
|
|
94
|
+
"periodic": {
|
|
95
|
+
"updatesDependencies": "2026-01-31T09:15:00.000Z"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
Skip this step if the update was abandoned or `ci-lint` still fails — the date records a completed
|
|
101
|
+
update, not an attempted one.
|
|
102
|
+
|
|
78
103
|
## What to report
|
|
79
104
|
|
|
80
105
|
Produce a written summary:
|
package/package.json
CHANGED
package/policy/policy.json
CHANGED
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
"ciLint": {
|
|
108
108
|
"required": {
|
|
109
109
|
"php": ["pint --parallel", "phpstan"],
|
|
110
|
-
"js": [
|
|
110
|
+
"js": []
|
|
111
111
|
},
|
|
112
112
|
"linters": {
|
|
113
113
|
"js": {
|
|
@@ -115,6 +115,12 @@
|
|
|
115
115
|
"eslint": "eslint"
|
|
116
116
|
}
|
|
117
117
|
},
|
|
118
|
+
"typeChecker": {
|
|
119
|
+
"js": {
|
|
120
|
+
"command": "tsc",
|
|
121
|
+
"extensions": [".ts", ".tsx", ".mts", ".cts"]
|
|
122
|
+
}
|
|
123
|
+
},
|
|
118
124
|
"baselineCommand": {
|
|
119
125
|
"js": "baseline check"
|
|
120
126
|
}
|
|
@@ -212,7 +212,7 @@
|
|
|
212
212
|
"ciLint": {
|
|
213
213
|
"type": "object",
|
|
214
214
|
"additionalProperties": false,
|
|
215
|
-
"required": ["required", "linters", "baselineCommand"],
|
|
215
|
+
"required": ["required", "linters", "typeChecker", "baselineCommand"],
|
|
216
216
|
"properties": {
|
|
217
217
|
"required": {
|
|
218
218
|
"type": "object",
|
|
@@ -237,6 +237,27 @@
|
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
239
|
},
|
|
240
|
+
"typeChecker": {
|
|
241
|
+
"type": "object",
|
|
242
|
+
"additionalProperties": false,
|
|
243
|
+
"required": ["js"],
|
|
244
|
+
"description": "The command ci-lint must run once the project contains a file with one of these extensions, outside node_modules and dot-directories. A plain JS project has nothing for it to check.",
|
|
245
|
+
"properties": {
|
|
246
|
+
"js": {
|
|
247
|
+
"type": "object",
|
|
248
|
+
"additionalProperties": false,
|
|
249
|
+
"required": ["command", "extensions"],
|
|
250
|
+
"properties": {
|
|
251
|
+
"command": { "type": "string" },
|
|
252
|
+
"extensions": {
|
|
253
|
+
"type": "array",
|
|
254
|
+
"items": { "type": "string" },
|
|
255
|
+
"minItems": 1
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
},
|
|
240
261
|
"baselineCommand": {
|
|
241
262
|
"type": "object",
|
|
242
263
|
"additionalProperties": false,
|