docs: private security channel, guarded --write-baseline, changelog

This commit is contained in:
rmyndharis
2026-06-14 14:55:22 +07:00
parent 1fa6f78803
commit 5a6e713eab
4 changed files with 72 additions and 5 deletions
+28
View File
@@ -0,0 +1,28 @@
# Changelog
All notable changes to this project are documented here.
## [Unreleased]
### Added
- `node:test` test suite covering the CLI, skill parsing, catalog generation, and the drift guard (`npm test`).
- `lib/skill-schema.js` as the single source of frontmatter field rules.
- `scripts/check-catalog-drift.js` (`npm run check:catalog`) — CI fails if committed catalog artifacts are stale.
- `install --force` to overwrite already-installed skills.
- `AG_SKILLS_DIR` environment variable to override the install destination (reported by `doctor`).
- Validator now checks that backticked referenced files (`resources/`, `references/`, `assets/`, `scripts/`) exist.
- `SECURITY.md` private vulnerability disclosure channel.
### Changed
- `install` now skips already-installed skills by default (use `--force` to overwrite) and reports an honest installed/skipped/failed summary.
- `install` exits non-zero when nothing could be installed; no longer prints "Installation complete!" on total failure.
- `update` exits non-zero on real failures (no installation, invalid name, not installed).
- Search scoring now also matches the catalog `triggers` field.
- Corrupt `bundles.json`/`aliases.json` now produce an explicit warning instead of degrading silently.
- `detectCategory` no longer treats generic "compliance" as `security`.
- `--write-baseline` now requires `--yes` and prints what it would grandfather.
### Fixed
- `listSkillIds`/`readSkill` no longer crash on a skill directory missing `SKILL.md`; `build-catalog` surfaces frontmatter parse warnings.
- Removed 228 dangling helper-file references across 165 skills.
- Removed dead code: `parseInlineList`/`stripQuotes` exports and the unreachable `name !== id` alias branch.
+3 -1
View File
@@ -12,4 +12,6 @@ This repository ships skills that may recommend terminal commands or infrastruct
## Reporting
If you discover a security issue in this repository, please open an issue with details and reproduction steps.
For routine, non-sensitive issues, open a GitHub issue with details and reproduction steps.
For sensitive vulnerabilities (anything that could be exploited before a fix ships), report privately by email to **yudhi@rmyndharis.com** instead of opening a public issue. Include reproduction steps and impact. Please allow a reasonable window for a fix before any public disclosure.
+18 -4
View File
@@ -20,6 +20,8 @@ const isStrict = process.argv.includes('--strict')
const writeBaseline = process.argv.includes('--write-baseline')
|| process.env.WRITE_BASELINE === '1'
|| process.env.WRITE_BASELINE === 'true';
const confirmWriteBaseline = process.argv.includes('--yes')
|| process.env.WRITE_BASELINE_YES === '1';
function isPlainObject(value) {
return value && typeof value === 'object' && !Array.isArray(value);
@@ -250,15 +252,27 @@ addStrictSectionErrors('Instructions', missingInstructionsSection, baselineInstr
addStrictSectionErrors(`SKILL.md line count <= ${LIMITS.skillLines}`, longFiles, baselineLongFile);
if (writeBaseline) {
const baselineData = {
generatedAt: new Date().toISOString(),
const toGrandfather = {
useSection: [...missingUseSection].sort(),
doNotUseSection: [...missingDoNotUseSection].sort(),
instructionsSection: [...missingInstructionsSection].sort(),
longFile: [...longFiles].sort(),
};
fs.writeFileSync(BASELINE_PATH, JSON.stringify(baselineData, null, 2));
console.log(`Baseline written to ${BASELINE_PATH}`);
const totalGrandfathered = Object.values(toGrandfather).reduce((sum, arr) => sum + arr.length, 0);
console.warn('\n⚠ --write-baseline will grandfather the following soft violations,');
console.warn(' silencing them in strict mode (CI). Review carefully:');
for (const [key, arr] of Object.entries(toGrandfather)) {
console.warn(` - ${key}: ${arr.length}`);
}
if (!confirmWriteBaseline) {
console.warn(`\nNot written. Re-run with --yes to grandfather ${totalGrandfathered} violation(s).`);
} else {
const baselineData = { generatedAt: new Date().toISOString(), ...toGrandfather };
fs.writeFileSync(BASELINE_PATH, JSON.stringify(baselineData, null, 2));
console.log(`Baseline written to ${BASELINE_PATH}`);
}
}
if (warnings.length) {
+23
View File
@@ -0,0 +1,23 @@
const test = require('node:test');
const assert = require('node:assert');
const { spawnSync } = require('node:child_process');
const fs = require('node:fs');
const path = require('node:path');
const ROOT = path.resolve(__dirname, '..');
const VALIDATOR = path.join(ROOT, 'scripts', 'validate-skills.js');
const BASELINE = path.join(ROOT, 'validation-baseline.json');
function runValidator(args) {
const result = spawnSync('node', [VALIDATOR, ...args], { encoding: 'utf8', cwd: ROOT });
return { code: result.status || 0, out: (result.stdout || '') + (result.stderr || '') };
}
test('--write-baseline without --yes does NOT write the baseline file', () => {
const before = fs.readFileSync(BASELINE, 'utf8');
const r = runValidator(['--write-baseline']);
const after = fs.readFileSync(BASELINE, 'utf8');
assert.strictEqual(after, before, 'baseline must be unchanged without --yes');
assert.ok(/Not written/.test(r.out), 'should explain it was not written');
assert.ok(/--yes/.test(r.out), 'should mention the --yes flag');
});