@rtorcato/repo-tooling 3.26.0 → 3.27.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/AGENTS.md
CHANGED
|
@@ -18,7 +18,7 @@ Every command supports `--json` and a non-interactive mode. Combine with `--yes`
|
|
|
18
18
|
| `setup --config <path>` | ✅ | `--dry-run` only | Scaffold with a full `ProjectConfig` JSON file. See `setup --config-schema`. |
|
|
19
19
|
| `setup --config-schema` | ✅ | ✅ (JSON Schema) | Print the JSON Schema for `ProjectConfig`. Use to validate configs before scaffolding. |
|
|
20
20
|
| `setup --dry-run` | ✅ | ✅ | Print resolved config + file list without writing. Pair with `--preset` or `--config`. |
|
|
21
|
-
| `doctor --json` | ✅ | ✅ | Audit a project. Returns `{ directory, results: [{ check, status, detail, hint? }] }`. Status: `ok` / `drift` / `missing` / `optional-missing`. |
|
|
21
|
+
| `doctor --json` | ✅ | ✅ | Audit a project. Returns `{ directory, results: [{ check, status, detail, hint? }] }`. Status: `ok` / `drift` / `missing` / `optional-missing` / `declared`. |
|
|
22
22
|
| `fix --json --yes` | ✅ | ✅ | Walk every doctor finding, apply fixers. Returns `FixActionRecord[]` with `status: applied | dry-run | skipped | already-ok | unsupported`. |
|
|
23
23
|
| `fix <target> --json --yes` | ✅ | ✅ | Apply one fixer. Targets from `list --json`. |
|
|
24
24
|
| `fix --dry-run` | ✅ | ✅ | Print what each fixer would write without writing. Combine with `--json`. |
|
|
@@ -156,6 +156,40 @@ function demoteDeclined(results, lock) {
|
|
|
156
156
|
};
|
|
157
157
|
});
|
|
158
158
|
}
|
|
159
|
+
// Declared exceptions (#558): a failing check the lock names is reported as
|
|
160
|
+
// `declared` with its reason — shown, never hidden, but no longer failing the
|
|
161
|
+
// run. An exception naming a check this run doesn't know is itself drift:
|
|
162
|
+
// otherwise a typo silently does nothing and a check rename silently
|
|
163
|
+
// un-suppresses a finding, and both are invisible.
|
|
164
|
+
function applyExceptions(results, lock) {
|
|
165
|
+
const exceptions = lock?.exceptions;
|
|
166
|
+
if (!exceptions)
|
|
167
|
+
return results;
|
|
168
|
+
const known = new Set(results.map((r) => r.check));
|
|
169
|
+
const overlaid = results.map((r) => {
|
|
170
|
+
const reason = exceptions[r.check];
|
|
171
|
+
if (!reason || r.status === 'ok')
|
|
172
|
+
return r;
|
|
173
|
+
// Hint deliberately dropped: the deviation is declared, so "how to fix it"
|
|
174
|
+
// is exactly the noise the exception exists to retire.
|
|
175
|
+
return {
|
|
176
|
+
check: r.check,
|
|
177
|
+
status: 'declared',
|
|
178
|
+
detail: `${r.detail} — declared exception: ${reason}`,
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
for (const name of Object.keys(exceptions)) {
|
|
182
|
+
if (known.has(name))
|
|
183
|
+
continue;
|
|
184
|
+
overlaid.push({
|
|
185
|
+
check: 'Declared exceptions',
|
|
186
|
+
status: 'drift',
|
|
187
|
+
detail: `.repo-tooling.json declares an exception for "${name}", which is not a check this run knows`,
|
|
188
|
+
hint: 'A typo, or a check that was renamed or removed — fix or delete the entry in `exceptions`',
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
return overlaid;
|
|
192
|
+
}
|
|
159
193
|
// The language-agnostic checks (src/base): repo hygiene, git hooks, CI,
|
|
160
194
|
// security, and GitHub repo-settings that apply to any repo regardless of
|
|
161
195
|
// language. Declared once and run for every project — a language module layers
|
|
@@ -238,7 +272,7 @@ export async function runDoctor(dir, skillsDir) {
|
|
|
238
272
|
skillsDir,
|
|
239
273
|
})),
|
|
240
274
|
];
|
|
241
|
-
return demoteDeclined(results, lock);
|
|
275
|
+
return applyExceptions(demoteDeclined(results, lock), lock);
|
|
242
276
|
}
|
|
243
277
|
// Swift suite (#286): base checks plus the module's own. Swift repos have no
|
|
244
278
|
// package.json, so nothing JS-shaped runs.
|
|
@@ -262,7 +296,7 @@ export async function runDoctor(dir, skillsDir) {
|
|
|
262
296
|
})),
|
|
263
297
|
...(await runSwiftChecks(targetDir)),
|
|
264
298
|
];
|
|
265
|
-
return demoteDeclined(results, lock);
|
|
299
|
+
return applyExceptions(demoteDeclined(results, lock), lock);
|
|
266
300
|
}
|
|
267
301
|
// Python suite (#290): same shape as Swift — base checks plus the module's
|
|
268
302
|
// own, and nothing JS-shaped, because a Python repo has no package.json.
|
|
@@ -286,7 +320,7 @@ export async function runDoctor(dir, skillsDir) {
|
|
|
286
320
|
})),
|
|
287
321
|
...(await runPythonChecks(targetDir)),
|
|
288
322
|
];
|
|
289
|
-
return demoteDeclined(results, lock);
|
|
323
|
+
return applyExceptions(demoteDeclined(results, lock), lock);
|
|
290
324
|
}
|
|
291
325
|
// Perl suite (#289): same shape as Swift and Python — base checks plus the
|
|
292
326
|
// module's own, and nothing JS-shaped, because a distribution has no
|
|
@@ -312,7 +346,7 @@ export async function runDoctor(dir, skillsDir) {
|
|
|
312
346
|
})),
|
|
313
347
|
...(await runPerlChecks(targetDir)),
|
|
314
348
|
];
|
|
315
|
-
return demoteDeclined(results, lock);
|
|
349
|
+
return applyExceptions(demoteDeclined(results, lock), lock);
|
|
316
350
|
}
|
|
317
351
|
// JS suite: the module's own checks, then the shared base ones. Only the
|
|
318
352
|
// JS-shaped checks are listed here — re-listing the base suite is what made
|
|
@@ -371,13 +405,14 @@ export async function runDoctor(dir, skillsDir) {
|
|
|
371
405
|
codeqlLanguages: languageModule.codeqlLanguages,
|
|
372
406
|
skillsDir,
|
|
373
407
|
})));
|
|
374
|
-
return demoteDeclined(results, lock);
|
|
408
|
+
return applyExceptions(demoteDeclined(results, lock), lock);
|
|
375
409
|
}
|
|
376
410
|
const STATUS_ICONS = {
|
|
377
411
|
ok: chalk.green('✅'),
|
|
378
412
|
drift: chalk.yellow('⚠️ '),
|
|
379
413
|
missing: chalk.red('❌'),
|
|
380
414
|
'optional-missing': chalk.gray('➖'),
|
|
415
|
+
declared: chalk.blue('📝'),
|
|
381
416
|
};
|
|
382
417
|
function statusLabel(status) {
|
|
383
418
|
switch (status) {
|
|
@@ -389,6 +424,8 @@ function statusLabel(status) {
|
|
|
389
424
|
return chalk.red('missing');
|
|
390
425
|
case 'optional-missing':
|
|
391
426
|
return chalk.gray('not configured');
|
|
427
|
+
case 'declared':
|
|
428
|
+
return chalk.blue('declared');
|
|
392
429
|
}
|
|
393
430
|
}
|
|
394
431
|
const MAX_NEXT_STEP_SUGGESTIONS = 8;
|
|
@@ -421,6 +458,7 @@ export function summarize(results) {
|
|
|
421
458
|
drift: results.filter((r) => r.status === 'drift').length,
|
|
422
459
|
missing: results.filter((r) => r.status === 'missing').length,
|
|
423
460
|
optionalMissing: results.filter((r) => r.status === 'optional-missing').length,
|
|
461
|
+
declared: results.filter((r) => r.status === 'declared').length,
|
|
424
462
|
};
|
|
425
463
|
}
|
|
426
464
|
export async function doctorCommand(options = {}) {
|
|
@@ -440,7 +478,7 @@ export async function doctorCommand(options = {}) {
|
|
|
440
478
|
}
|
|
441
479
|
const summary = summarize(results);
|
|
442
480
|
console.log();
|
|
443
|
-
console.log(` Summary: ${chalk.green(`${summary.ok} ok`)}, ${chalk.yellow(`${summary.drift} drift`)}, ${chalk.red(`${summary.missing} missing`)}, ${chalk.gray(`${summary.optionalMissing} not configured`)}\n`);
|
|
481
|
+
console.log(` Summary: ${chalk.green(`${summary.ok} ok`)}, ${chalk.yellow(`${summary.drift} drift`)}, ${chalk.red(`${summary.missing} missing`)}, ${chalk.gray(`${summary.optionalMissing} not configured`)}, ${chalk.blue(`${summary.declared} declared`)}\n`);
|
|
444
482
|
const suggestions = nextStepSuggestions(results, await detectLanguage(dir));
|
|
445
483
|
if (suggestions.length > 0) {
|
|
446
484
|
console.log(chalk.bold(' Next steps:'));
|
package/dist/cli/commands/fix.js
CHANGED
|
@@ -436,7 +436,10 @@ export async function fixCommand(target, options = {}) {
|
|
|
436
436
|
console.log();
|
|
437
437
|
return;
|
|
438
438
|
}
|
|
439
|
-
|
|
439
|
+
// `declared` is a deviation the lockfile records on purpose (#558) — a bulk
|
|
440
|
+
// fix must not "repair" it. A targeted `fix <target>` still can: naming the
|
|
441
|
+
// fixer is the same explicit override the declined-in-lock path gets.
|
|
442
|
+
const fixable = results.filter((r) => r.status !== 'ok' && r.status !== 'declared');
|
|
440
443
|
if (fixable.length === 0) {
|
|
441
444
|
if (json)
|
|
442
445
|
return emitJson(null);
|
|
@@ -117,6 +117,11 @@ export function lockfileSchema() {
|
|
|
117
117
|
},
|
|
118
118
|
},
|
|
119
119
|
},
|
|
120
|
+
exceptions: {
|
|
121
|
+
type: 'object',
|
|
122
|
+
additionalProperties: { type: 'string', minLength: 1 },
|
|
123
|
+
description: 'Declared exceptions: doctor check name → the reason this repo deliberately deviates. The reason is mandatory and non-empty — doctor shows the check as `declared` with it (never hidden) and stops failing the run for it. An entry naming a check doctor does not run is itself reported as drift.',
|
|
124
|
+
},
|
|
120
125
|
writtenBy: {
|
|
121
126
|
type: 'string',
|
|
122
127
|
description: 'Package name and version that last wrote this file.',
|
|
@@ -193,6 +198,7 @@ export async function writeLockfile(dir, config, assets) {
|
|
|
193
198
|
...(existing?.aiLoop ? { aiLoop: existing.aiLoop } : {}),
|
|
194
199
|
...(existing?.requiredSkills ? { requiredSkills: existing.requiredSkills } : {}),
|
|
195
200
|
...(existing?.mcp ? { mcp: existing.mcp } : {}),
|
|
201
|
+
...(existing?.exceptions ? { exceptions: existing.exceptions } : {}),
|
|
196
202
|
writtenBy: `@rtorcato/repo-tooling@${packageJson.version}`,
|
|
197
203
|
writtenAt: new Date().toISOString(),
|
|
198
204
|
};
|
package/package.json
CHANGED
|
@@ -30,6 +30,8 @@ npx @rtorcato/repo-tooling doctor --json # confirm clean
|
|
|
30
30
|
prompt to **No**; `--yes` is required to overwrite. Show `fix <target> --diff` first.
|
|
31
31
|
- `missing` — required and absent → fix it.
|
|
32
32
|
- `optional-missing` — opt-in tool not configured. Only fix if the user wants that tool.
|
|
33
|
+
- `declared` — a real deviation the repo's `.repo-tooling.json` `exceptions` records on
|
|
34
|
+
purpose, with its reason. Leave it alone; it doesn't fail the run.
|
|
33
35
|
|
|
34
36
|
`fix` returns `FixActionRecord[]` with `status: applied | dry-run | skipped | already-ok | unsupported`.
|
|
35
37
|
|