Reorganize: Move all skills to skills/ folder

- Created skills/ directory
- Moved 272 skills to skills/ subfolder
- Kept agents/ at root level
- Kept installation scripts and docs at root level

Repository structure:
- skills/           - All 272 skills from skills.sh
- agents/           - Agent definitions
- *.sh, *.ps1       - Installation scripts
- README.md, etc.   - Documentation

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
admin
2026-01-23 18:05:17 +00:00
Unverified
parent 2b4e974878
commit b723e2bd7d
4083 changed files with 1056 additions and 1098063 deletions

View File

@@ -0,0 +1,416 @@
import { describe, expect, test } from 'bun:test';
import { checkCustomRules } from '../src/core/rules-custom.ts';
import type { CustomRule } from '../src/types.ts';
describe('custom rule matching', () => {
test('basic command match', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all'],
reason: 'Use specific files.',
},
];
const result = checkCustomRules(['git', 'add', '-A'], rules);
expect(result).toBe('[block-git-add-all] Use specific files.');
});
test('match with long option form', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all'],
reason: 'Use specific files.',
},
];
const result = checkCustomRules(['git', 'add', '--all'], rules);
expect(result).toBe('[block-git-add-all] Use specific files.');
});
test('no match when command differs', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'test',
},
];
const result = checkCustomRules(['npm', 'add', '-A'], rules);
expect(result).toBeNull();
});
test('no match when subcommand differs', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'test',
},
];
const result = checkCustomRules(['git', 'commit', '-A'], rules);
expect(result).toBeNull();
});
test('no match when no blocked args present', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-all',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all'],
reason: 'test',
},
];
const result = checkCustomRules(['git', 'add', 'file.txt'], rules);
expect(result).toBeNull();
});
test('rule without subcommand matches any invocation', () => {
const rules: CustomRule[] = [
{
name: 'block-npm-global',
command: 'npm',
subcommand: undefined,
block_args: ['-g', '--global'],
reason: 'No global installs.',
},
];
// Match with install subcommand
let result = checkCustomRules(['npm', 'install', '-g', 'pkg'], rules);
expect(result).toBe('[block-npm-global] No global installs.');
// Match with uninstall subcommand too
result = checkCustomRules(['npm', 'uninstall', '-g', 'pkg'], rules);
expect(result).toBe('[block-npm-global] No global installs.');
});
test('multiple rules first match wins', () => {
const rules: CustomRule[] = [
{
name: 'rule1',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'Rule 1 reason',
},
{
name: 'rule2',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'Rule 2 reason',
},
];
const result = checkCustomRules(['git', 'add', '-A'], rules);
expect(result).toBe('[rule1] Rule 1 reason');
});
test('case sensitive command matching', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: undefined,
block_args: ['-A'],
reason: 'test',
},
];
// Lowercase git matches
let result = checkCustomRules(['git', '-A'], rules);
expect(result).toBe('[test] test');
// Uppercase GIT does NOT match (case-sensitive)
result = checkCustomRules(['GIT', '-A'], rules);
expect(result).toBeNull();
});
test('case sensitive arg matching', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: undefined,
block_args: ['-A'],
reason: 'test',
},
];
// -A matches
let result = checkCustomRules(['git', '-A'], rules);
expect(result).not.toBeNull();
// -a does NOT match
result = checkCustomRules(['git', '-a'], rules);
expect(result).toBeNull();
});
test('args with values can be matched', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'docker',
subcommand: 'run',
block_args: ['--privileged'],
reason: 'No privileged mode.',
},
];
const result = checkCustomRules(['docker', 'run', '--privileged', 'image'], rules);
expect(result).toBe('[test] No privileged mode.');
});
test('subcommand with options before - git -C handled correctly', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'push',
block_args: ['--force'],
reason: 'No force push.',
},
];
// git -C /path push --force: correctly identifies push as subcommand
let result = checkCustomRules(['git', '-C', '/path', 'push', '--force'], rules);
expect(result).toBe('[test] No force push.');
// Attached form -C/path also works
result = checkCustomRules(['git', '-C/path', 'push', '--force'], rules);
expect(result).toBe('[test] No force push.');
});
test('docker compose pattern', () => {
const rules: CustomRule[] = [
{
name: 'block-docker-compose-up',
command: 'docker',
subcommand: 'compose',
block_args: ['up'],
reason: 'No docker compose up.',
},
];
const result = checkCustomRules(['docker', 'compose', 'up', '-d'], rules);
expect(result).toBe('[block-docker-compose-up] No docker compose up.');
});
test('empty tokens returns null', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: undefined,
block_args: ['-A'],
reason: 'test',
},
];
const result = checkCustomRules([], rules);
expect(result).toBeNull();
});
test('empty rules returns null', () => {
const result = checkCustomRules(['git', 'add', '-A'], []);
expect(result).toBeNull();
});
test('command with path normalized', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: undefined,
block_args: ['-A'],
reason: 'test',
},
];
const result = checkCustomRules(['/usr/bin/git', '-A'], rules);
expect(result).toBe('[test] test');
});
test('block args with equals value', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'npm',
subcommand: 'config',
block_args: ['--location=global'],
reason: 'No global config.',
},
];
const tokens = ['npm', 'config', 'set', '--location=global'];
const result = checkCustomRules(tokens, rules);
expect(result).toBe('[test] No global config.');
});
test('block dot for git add', () => {
const rules: CustomRule[] = [
{
name: 'block-git-add-dot',
command: 'git',
subcommand: 'add',
block_args: ['.'],
reason: 'Use specific files.',
},
];
let result = checkCustomRules(['git', 'add', '.'], rules);
expect(result).toBe('[block-git-add-dot] Use specific files.');
// git add file.txt should pass
result = checkCustomRules(['git', 'add', 'file.txt'], rules);
expect(result).toBeNull();
});
test('multiple blocked args any matches', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'add',
block_args: ['-A', '--all', '.', '-u'],
reason: 'No blanket add.',
},
];
// Each blocked arg should trigger
for (const arg of ['-A', '--all', '.', '-u']) {
const result = checkCustomRules(['git', 'add', arg], rules);
expect(result).not.toBeNull();
}
});
test('combined short options expanded', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'test',
},
];
// -Ap contains -A, so it should be blocked
const result = checkCustomRules(['git', 'add', '-Ap'], rules);
expect(result).toBe('[test] test');
});
test('combined short options case sensitive', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'add',
block_args: ['-A'],
reason: 'test',
},
];
// -ap does NOT contain -A (lowercase a != uppercase A)
const result = checkCustomRules(['git', 'add', '-ap'], rules);
expect(result).toBeNull();
});
test('combined short options multiple flags', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'add',
block_args: ['-u'],
reason: 'test',
},
];
// -Aup contains -u
const result = checkCustomRules(['git', 'add', '-Aup'], rules);
expect(result).toBe('[test] test');
});
test('long options not expanded', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'add',
block_args: ['--all'],
reason: 'test',
},
];
// --all-files is not --all
const result = checkCustomRules(['git', 'add', '--all-files'], rules);
expect(result).toBeNull();
});
test('subcommand after double dash', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'checkout',
block_args: ['--force'],
reason: 'test',
},
];
// git -- checkout --force: subcommand is checkout after --
const result = checkCustomRules(['git', '--', 'checkout', '--force'], rules);
expect(result).toBe('[test] test');
});
test('no subcommand after double dash at end', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'push',
block_args: ['--force'],
reason: 'test',
},
];
const result = checkCustomRules(['git', '--'], rules);
expect(result).toBeNull();
});
test('long option with equals', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'push',
block_args: ['--force'],
reason: 'test',
},
];
const result = checkCustomRules(['git', '--config=foo', 'push', '--force'], rules);
expect(result).toBe('[test] test');
});
test('long option without equals', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'push',
block_args: ['--force'],
reason: 'test',
},
];
// --verbose is a flag, push is subcommand
const result = checkCustomRules(['git', '--verbose', 'push', '--force'], rules);
expect(result).toBe('[test] test');
});
test('attached short option value', () => {
const rules: CustomRule[] = [
{
name: 'test',
command: 'git',
subcommand: 'push',
block_args: ['--force'],
reason: 'test',
},
];
// -C/path is attached, so push is next
const result = checkCustomRules(['git', '-C/path', 'push', '--force'], rules);
expect(result).toBe('[test] test');
});
});