kastell 2.2.4 → 2.2.5

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.
Files changed (75) hide show
  1. package/.claude-plugin/marketplace.json +18 -18
  2. package/.claude-plugin/plugin.json +45 -38
  3. package/CHANGELOG.md +1294 -1266
  4. package/LICENSE +201 -201
  5. package/NOTICE +5 -5
  6. package/README.md +1 -1
  7. package/README.tr.md +1 -1
  8. package/bin/kastell +2 -2
  9. package/bin/kastell-mcp +5 -5
  10. package/dist/adapters/coolify.js +92 -92
  11. package/dist/adapters/dokploy.js +99 -99
  12. package/dist/core/audit/formatters/badge.js +20 -20
  13. package/dist/core/completions.js +631 -631
  14. package/dist/mcp/server.d.ts.map +1 -1
  15. package/dist/mcp/server.js +25 -31
  16. package/dist/mcp/server.js.map +1 -1
  17. package/dist/mcp/tools/serverExplain.d.ts.map +1 -1
  18. package/dist/mcp/tools/serverExplain.js.map +1 -1
  19. package/dist/mcp/tools/serverFleet.d.ts.map +1 -1
  20. package/dist/mcp/tools/serverFleet.js.map +1 -1
  21. package/dist/mcp/tools/serverInfo.d.ts +1 -1
  22. package/dist/mcp/tools/serverInfo.js +1 -1
  23. package/dist/mcp/tools/serverPlugin.d.ts.map +1 -1
  24. package/dist/mcp/tools/serverPlugin.js.map +1 -1
  25. package/dist/mcp-bundle.mjs +101015 -0
  26. package/dist/utils/cloudInit.js +58 -58
  27. package/dist/utils/version.d.ts.map +1 -1
  28. package/dist/utils/version.js +19 -4
  29. package/dist/utils/version.js.map +1 -1
  30. package/kastell-plugin/.claude-plugin/plugin.json +20 -20
  31. package/kastell-plugin/.mcp.json +15 -8
  32. package/kastell-plugin/README.md +113 -113
  33. package/kastell-plugin/agents/kastell-auditor.md +77 -77
  34. package/kastell-plugin/agents/scripts/bucket_mapper.sh +101 -101
  35. package/kastell-plugin/agents/scripts/trend_report.sh +91 -91
  36. package/kastell-plugin/hooks/destroy-block.cjs +31 -31
  37. package/kastell-plugin/hooks/hooks.json +57 -57
  38. package/kastell-plugin/hooks/pre-commit-audit-guard.cjs +75 -75
  39. package/kastell-plugin/hooks/session-audit.cjs +86 -86
  40. package/kastell-plugin/hooks/session-log.cjs +56 -56
  41. package/kastell-plugin/hooks/stop-quality-check.cjs +72 -72
  42. package/kastell-plugin/skills/kastell-careful/SKILL.md +64 -64
  43. package/kastell-plugin/skills/kastell-ops/SKILL.md +139 -139
  44. package/kastell-plugin/skills/kastell-ops/references/commands.md +45 -45
  45. package/kastell-plugin/skills/kastell-ops/references/mcp-tools.md +50 -50
  46. package/kastell-plugin/skills/kastell-ops/references/patterns.md +145 -145
  47. package/kastell-plugin/skills/kastell-ops/references/pitfalls.md +136 -136
  48. package/kastell-plugin/skills/kastell-ops/scripts/check_coverage.sh +101 -101
  49. package/kastell-plugin/skills/kastell-ops/scripts/fleet_report.sh +73 -73
  50. package/kastell-plugin/skills/kastell-ops/scripts/parse_audit.sh +76 -76
  51. package/kastell-plugin/skills/kastell-research/SKILL.md +90 -90
  52. package/kastell-plugin/skills/kastell-scaffold/SKILL.md +104 -104
  53. package/kastell-plugin/skills/kastell-scaffold/references/template-audit-check.md +150 -150
  54. package/kastell-plugin/skills/kastell-scaffold/references/template-command.md +80 -80
  55. package/kastell-plugin/skills/kastell-scaffold/references/template-mcp-tool.md +72 -72
  56. package/kastell-plugin/skills/kastell-scaffold/references/template-provider.md +67 -67
  57. package/kastell-plugin/skills/kastell-scaffold/scripts/scaffold.sh +180 -180
  58. package/kastell-plugin/skills/kastell-scaffold/templates/check-test.ts.tpl +27 -27
  59. package/kastell-plugin/skills/kastell-scaffold/templates/check.ts.tpl +50 -50
  60. package/kastell-plugin/skills/kastell-scaffold/templates/command-core.ts.tpl +18 -18
  61. package/kastell-plugin/skills/kastell-scaffold/templates/command-test.ts.tpl +17 -17
  62. package/kastell-plugin/skills/kastell-scaffold/templates/command.ts.tpl +25 -25
  63. package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool-test.ts.tpl +30 -30
  64. package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool.ts.tpl +29 -29
  65. package/kastell-plugin/skills/kastell-scaffold/templates/provider-test.ts.tpl +34 -34
  66. package/kastell-plugin/skills/kastell-scaffold/templates/provider.ts.tpl +32 -32
  67. package/package.json +125 -122
  68. package/dist/commands/interactive.d.ts +0 -11
  69. package/dist/commands/interactive.d.ts.map +0 -1
  70. package/dist/commands/interactive.js +0 -1079
  71. package/dist/commands/interactive.js.map +0 -1
  72. package/dist/core/lock.d.ts +0 -66
  73. package/dist/core/lock.d.ts.map +0 -1
  74. package/dist/core/lock.js +0 -556
  75. package/dist/core/lock.js.map +0 -1
@@ -1,101 +1,101 @@
1
- #!/usr/bin/env bash
2
- # bucket_mapper.sh — Map kastell audit JSON checks to 5 security buckets.
3
- # Usage: kastell audit --server <name> --json | bash bucket_mapper.sh
4
- # OR: bash bucket_mapper.sh < audit-output.json
5
- # OR: bash bucket_mapper.sh audit-output.json
6
- #
7
- # Output: Per-bucket check list with pass/fail status and severity.
8
- # Used by kastell-auditor agent for structured analysis.
9
- #
10
- # Requires: node
11
-
12
- set -euo pipefail
13
-
14
- if [[ -n "${1:-}" && -f "$1" ]]; then
15
- INPUT=$(cat "$1")
16
- elif [[ ! -t 0 ]]; then
17
- INPUT=$(cat)
18
- else
19
- echo "Usage: kastell audit --server <name> --json | bash bucket_mapper.sh" >&2
20
- exit 1
21
- fi
22
-
23
- node -e "
24
- const data = JSON.parse(process.argv[1]);
25
- const checks = data.checks || data.results || [];
26
-
27
- const BUCKETS = {
28
- '1_Perimeter': {
29
- match: ['network', 'firewall', 'dns'],
30
- checks: []
31
- },
32
- '2_Authentication': {
33
- match: ['ssh', 'auth', 'crypto', 'accounts'],
34
- checks: []
35
- },
36
- '3_Runtime': {
37
- match: ['docker', 'services', 'boot', 'scheduling'],
38
- checks: []
39
- },
40
- '4_Internals': {
41
- match: ['filesystem', 'logging', 'kernel', 'memory'],
42
- checks: []
43
- },
44
- '5_Compliance': {
45
- match: [], // catchall
46
- checks: []
47
- }
48
- };
49
-
50
- function getBucket(category) {
51
- const cat = (category || '').toLowerCase();
52
- for (const [name, bucket] of Object.entries(BUCKETS)) {
53
- if (name === '5_Compliance') continue;
54
- if (bucket.match.some(m => cat.includes(m))) return name;
55
- }
56
- return '5_Compliance';
57
- }
58
-
59
- // Map checks to buckets
60
- for (const c of checks) {
61
- const bucket = getBucket(c.category);
62
- BUCKETS[bucket].checks.push({
63
- id: c.id || 'unknown',
64
- name: c.name || '',
65
- severity: c.severity || 'info',
66
- passed: !!c.passed,
67
- category: c.category || ''
68
- });
69
- }
70
-
71
- // Output
72
- const score = data.score ?? data.overallScore ?? 'N/A';
73
- console.log('Score: ' + score + '/100');
74
- console.log('Total checks: ' + checks.length);
75
- console.log('');
76
-
77
- for (const [name, bucket] of Object.entries(BUCKETS)) {
78
- const label = name.replace(/^[0-9]_/, '');
79
- const passed = bucket.checks.filter(c => c.passed).length;
80
- const total = bucket.checks.length;
81
- const critFail = bucket.checks.filter(c => !c.passed && c.severity === 'critical');
82
-
83
- console.log('--- ' + label + ' (' + passed + '/' + total + ') ---');
84
-
85
- // Show failed checks (critical first, then warning)
86
- const failed = bucket.checks
87
- .filter(c => !c.passed)
88
- .sort((a, b) => {
89
- const sev = { critical: 0, warning: 1, info: 2 };
90
- return (sev[a.severity] ?? 3) - (sev[b.severity] ?? 3);
91
- });
92
-
93
- for (const c of failed.slice(0, 5)) {
94
- const icon = c.severity === 'critical' ? '!!' : c.severity === 'warning' ? '! ' : ' ';
95
- console.log(' [FAIL] ' + icon + c.id + ' — ' + c.name);
96
- }
97
- if (failed.length > 5) console.log(' ... and ' + (failed.length - 5) + ' more');
98
- if (failed.length === 0) console.log(' All checks passed');
99
- console.log('');
100
- }
101
- " "$INPUT"
1
+ #!/usr/bin/env bash
2
+ # bucket_mapper.sh — Map kastell audit JSON checks to 5 security buckets.
3
+ # Usage: kastell audit --server <name> --json | bash bucket_mapper.sh
4
+ # OR: bash bucket_mapper.sh < audit-output.json
5
+ # OR: bash bucket_mapper.sh audit-output.json
6
+ #
7
+ # Output: Per-bucket check list with pass/fail status and severity.
8
+ # Used by kastell-auditor agent for structured analysis.
9
+ #
10
+ # Requires: node
11
+
12
+ set -euo pipefail
13
+
14
+ if [[ -n "${1:-}" && -f "$1" ]]; then
15
+ INPUT=$(cat "$1")
16
+ elif [[ ! -t 0 ]]; then
17
+ INPUT=$(cat)
18
+ else
19
+ echo "Usage: kastell audit --server <name> --json | bash bucket_mapper.sh" >&2
20
+ exit 1
21
+ fi
22
+
23
+ node -e "
24
+ const data = JSON.parse(process.argv[1]);
25
+ const checks = data.checks || data.results || [];
26
+
27
+ const BUCKETS = {
28
+ '1_Perimeter': {
29
+ match: ['network', 'firewall', 'dns'],
30
+ checks: []
31
+ },
32
+ '2_Authentication': {
33
+ match: ['ssh', 'auth', 'crypto', 'accounts'],
34
+ checks: []
35
+ },
36
+ '3_Runtime': {
37
+ match: ['docker', 'services', 'boot', 'scheduling'],
38
+ checks: []
39
+ },
40
+ '4_Internals': {
41
+ match: ['filesystem', 'logging', 'kernel', 'memory'],
42
+ checks: []
43
+ },
44
+ '5_Compliance': {
45
+ match: [], // catchall
46
+ checks: []
47
+ }
48
+ };
49
+
50
+ function getBucket(category) {
51
+ const cat = (category || '').toLowerCase();
52
+ for (const [name, bucket] of Object.entries(BUCKETS)) {
53
+ if (name === '5_Compliance') continue;
54
+ if (bucket.match.some(m => cat.includes(m))) return name;
55
+ }
56
+ return '5_Compliance';
57
+ }
58
+
59
+ // Map checks to buckets
60
+ for (const c of checks) {
61
+ const bucket = getBucket(c.category);
62
+ BUCKETS[bucket].checks.push({
63
+ id: c.id || 'unknown',
64
+ name: c.name || '',
65
+ severity: c.severity || 'info',
66
+ passed: !!c.passed,
67
+ category: c.category || ''
68
+ });
69
+ }
70
+
71
+ // Output
72
+ const score = data.score ?? data.overallScore ?? 'N/A';
73
+ console.log('Score: ' + score + '/100');
74
+ console.log('Total checks: ' + checks.length);
75
+ console.log('');
76
+
77
+ for (const [name, bucket] of Object.entries(BUCKETS)) {
78
+ const label = name.replace(/^[0-9]_/, '');
79
+ const passed = bucket.checks.filter(c => c.passed).length;
80
+ const total = bucket.checks.length;
81
+ const critFail = bucket.checks.filter(c => !c.passed && c.severity === 'critical');
82
+
83
+ console.log('--- ' + label + ' (' + passed + '/' + total + ') ---');
84
+
85
+ // Show failed checks (critical first, then warning)
86
+ const failed = bucket.checks
87
+ .filter(c => !c.passed)
88
+ .sort((a, b) => {
89
+ const sev = { critical: 0, warning: 1, info: 2 };
90
+ return (sev[a.severity] ?? 3) - (sev[b.severity] ?? 3);
91
+ });
92
+
93
+ for (const c of failed.slice(0, 5)) {
94
+ const icon = c.severity === 'critical' ? '!!' : c.severity === 'warning' ? '! ' : ' ';
95
+ console.log(' [FAIL] ' + icon + c.id + ' — ' + c.name);
96
+ }
97
+ if (failed.length > 5) console.log(' ... and ' + (failed.length - 5) + ' more');
98
+ if (failed.length === 0) console.log(' All checks passed');
99
+ console.log('');
100
+ }
101
+ " "$INPUT"
@@ -1,91 +1,91 @@
1
- #!/usr/bin/env bash
2
- # trend_report.sh — Generate audit score trend from audit-history.json.
3
- # Usage: bash trend_report.sh [server-name]
4
- # OR: bash trend_report.sh --all
5
- #
6
- # Reads ~/.kastell/audit-history.json (maintained by kastell-auditor agent).
7
- # Shows score over time with delta indicators.
8
- #
9
- # Requires: node
10
-
11
- set -euo pipefail
12
-
13
- HISTORY_FILE="${KASTELL_HOME:-$HOME/.kastell}/audit-history.json"
14
- SERVER="${1:-}"
15
-
16
- if [[ ! -f "$HISTORY_FILE" ]]; then
17
- echo "No audit history found at: $HISTORY_FILE"
18
- echo "Run 'kastell audit --server <name>' to generate data."
19
- exit 0
20
- fi
21
-
22
- node -e "
23
- const fs = require('fs');
24
- const data = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
25
- const server = process.argv[2] || '';
26
- const showAll = server === '--all';
27
-
28
- if (!Array.isArray(data) || data.length === 0) {
29
- console.log('No audit history entries found.');
30
- process.exit(0);
31
- }
32
-
33
- // Filter by server if specified
34
- let entries = showAll ? data : server
35
- ? data.filter(e => e.serverName === server || e.server === server)
36
- : data;
37
-
38
- if (entries.length === 0) {
39
- console.log('No audit history for server: ' + server);
40
- console.log('Available servers: ' + [...new Set(data.map(e => e.serverName || e.server))].join(', '));
41
- process.exit(0);
42
- }
43
-
44
- // Sort by date
45
- entries.sort((a, b) => new Date(a.timestamp || a.date) - new Date(b.timestamp || b.date));
46
-
47
- // Group by server
48
- const byServer = {};
49
- for (const e of entries) {
50
- const name = e.serverName || e.server || 'unknown';
51
- if (!byServer[name]) byServer[name] = [];
52
- byServer[name].push(e);
53
- }
54
-
55
- console.log('=== Audit Score Trend ===');
56
- console.log('');
57
-
58
- for (const [name, history] of Object.entries(byServer)) {
59
- console.log('Server: ' + name);
60
- console.log('-'.repeat(50));
61
-
62
- let prev = null;
63
- for (const e of history) {
64
- const score = e.overallScore ?? e.score ?? 0;
65
- const date = (e.timestamp || e.date || '').split('T')[0];
66
- let delta = '';
67
- if (prev !== null) {
68
- const diff = score - prev;
69
- delta = diff > 0 ? ' (+' + diff + ')' : diff < 0 ? ' (' + diff + ')' : ' (=)';
70
- }
71
-
72
- // Visual bar
73
- const filled = Math.round(score / 5);
74
- const bar = '█'.repeat(filled) + '░'.repeat(20 - filled);
75
-
76
- console.log(' ' + date + ' ' + String(score).padStart(3) + '/100 ' + bar + delta);
77
- prev = score;
78
- }
79
-
80
- // Latest bucket scores if available
81
- const latest = history[history.length - 1];
82
- if (latest.bucketScores) {
83
- console.log('');
84
- console.log(' Latest bucket breakdown:');
85
- for (const [bucket, score] of Object.entries(latest.bucketScores)) {
86
- console.log(' ' + bucket.padEnd(15) + ': ' + score);
87
- }
88
- }
89
- console.log('');
90
- }
91
- " "$HISTORY_FILE" "$SERVER"
1
+ #!/usr/bin/env bash
2
+ # trend_report.sh — Generate audit score trend from audit-history.json.
3
+ # Usage: bash trend_report.sh [server-name]
4
+ # OR: bash trend_report.sh --all
5
+ #
6
+ # Reads ~/.kastell/audit-history.json (maintained by kastell-auditor agent).
7
+ # Shows score over time with delta indicators.
8
+ #
9
+ # Requires: node
10
+
11
+ set -euo pipefail
12
+
13
+ HISTORY_FILE="${KASTELL_HOME:-$HOME/.kastell}/audit-history.json"
14
+ SERVER="${1:-}"
15
+
16
+ if [[ ! -f "$HISTORY_FILE" ]]; then
17
+ echo "No audit history found at: $HISTORY_FILE"
18
+ echo "Run 'kastell audit --server <name>' to generate data."
19
+ exit 0
20
+ fi
21
+
22
+ node -e "
23
+ const fs = require('fs');
24
+ const data = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
25
+ const server = process.argv[2] || '';
26
+ const showAll = server === '--all';
27
+
28
+ if (!Array.isArray(data) || data.length === 0) {
29
+ console.log('No audit history entries found.');
30
+ process.exit(0);
31
+ }
32
+
33
+ // Filter by server if specified
34
+ let entries = showAll ? data : server
35
+ ? data.filter(e => e.serverName === server || e.server === server)
36
+ : data;
37
+
38
+ if (entries.length === 0) {
39
+ console.log('No audit history for server: ' + server);
40
+ console.log('Available servers: ' + [...new Set(data.map(e => e.serverName || e.server))].join(', '));
41
+ process.exit(0);
42
+ }
43
+
44
+ // Sort by date
45
+ entries.sort((a, b) => new Date(a.timestamp || a.date) - new Date(b.timestamp || b.date));
46
+
47
+ // Group by server
48
+ const byServer = {};
49
+ for (const e of entries) {
50
+ const name = e.serverName || e.server || 'unknown';
51
+ if (!byServer[name]) byServer[name] = [];
52
+ byServer[name].push(e);
53
+ }
54
+
55
+ console.log('=== Audit Score Trend ===');
56
+ console.log('');
57
+
58
+ for (const [name, history] of Object.entries(byServer)) {
59
+ console.log('Server: ' + name);
60
+ console.log('-'.repeat(50));
61
+
62
+ let prev = null;
63
+ for (const e of history) {
64
+ const score = e.overallScore ?? e.score ?? 0;
65
+ const date = (e.timestamp || e.date || '').split('T')[0];
66
+ let delta = '';
67
+ if (prev !== null) {
68
+ const diff = score - prev;
69
+ delta = diff > 0 ? ' (+' + diff + ')' : diff < 0 ? ' (' + diff + ')' : ' (=)';
70
+ }
71
+
72
+ // Visual bar
73
+ const filled = Math.round(score / 5);
74
+ const bar = '█'.repeat(filled) + '░'.repeat(20 - filled);
75
+
76
+ console.log(' ' + date + ' ' + String(score).padStart(3) + '/100 ' + bar + delta);
77
+ prev = score;
78
+ }
79
+
80
+ // Latest bucket scores if available
81
+ const latest = history[history.length - 1];
82
+ if (latest.bucketScores) {
83
+ console.log('');
84
+ console.log(' Latest bucket breakdown:');
85
+ for (const [bucket, score] of Object.entries(latest.bucketScores)) {
86
+ console.log(' ' + bucket.padEnd(15) + ': ' + score);
87
+ }
88
+ }
89
+ console.log('');
90
+ }
91
+ " "$HISTORY_FILE" "$SERVER"
@@ -1,31 +1,31 @@
1
- #!/usr/bin/env node
2
- // PreToolUse hook: Block kastell destroy / server-delete commands (hard block)
3
-
4
- // MANDATORY stdin guard — exit silently if stdin unavailable (e.g. after /clear)
5
- if (!process.stdin || process.stdin.destroyed || !process.stdin.readable) {
6
- process.exit(0);
7
- }
8
-
9
- let input = '';
10
- const stdinTimeout = setTimeout(() => process.exit(0), 1500);
11
- process.stdin.setEncoding('utf8');
12
- process.stdin.on('error', () => process.exit(0));
13
- process.stdin.on('data', chunk => { input += chunk; });
14
- process.stdin.on('end', () => {
15
- clearTimeout(stdinTimeout);
16
- try {
17
- const data = JSON.parse(input);
18
- const cmd = (data.tool_input && data.tool_input.command) || '';
19
-
20
- if (/\bkastell\s+(destroy|server-delete)\b/.test(cmd)) {
21
- process.stdout.write(JSON.stringify({
22
- decision: 'block',
23
- reason: 'Destructive kastell operation detected. Use kastell destroy with --force flag directly in terminal, not through Claude Code.',
24
- }));
25
- process.exit(2);
26
- }
27
- } catch {}
28
-
29
- // Not destructive or parse error — allow
30
- process.exit(0);
31
- });
1
+ #!/usr/bin/env node
2
+ // PreToolUse hook: Block kastell destroy / server-delete commands (hard block)
3
+
4
+ // MANDATORY stdin guard — exit silently if stdin unavailable (e.g. after /clear)
5
+ if (!process.stdin || process.stdin.destroyed || !process.stdin.readable) {
6
+ process.exit(0);
7
+ }
8
+
9
+ let input = '';
10
+ const stdinTimeout = setTimeout(() => process.exit(0), 1500);
11
+ process.stdin.setEncoding('utf8');
12
+ process.stdin.on('error', () => process.exit(0));
13
+ process.stdin.on('data', chunk => { input += chunk; });
14
+ process.stdin.on('end', () => {
15
+ clearTimeout(stdinTimeout);
16
+ try {
17
+ const data = JSON.parse(input);
18
+ const cmd = (data.tool_input && data.tool_input.command) || '';
19
+
20
+ if (/\bkastell\s+(destroy|server-delete)\b/.test(cmd)) {
21
+ process.stdout.write(JSON.stringify({
22
+ decision: 'block',
23
+ reason: 'Destructive kastell operation detected. Use kastell destroy with --force flag directly in terminal, not through Claude Code.',
24
+ }));
25
+ process.exit(2);
26
+ }
27
+ } catch {}
28
+
29
+ // Not destructive or parse error — allow
30
+ process.exit(0);
31
+ });
@@ -1,57 +1,57 @@
1
- {
2
- "hooks": {
3
- "PreToolUse": [
4
- {
5
- "matcher": "Bash",
6
- "if": "Bash(*kastell*)",
7
- "hooks": [
8
- {
9
- "type": "command",
10
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/destroy-block.cjs"
11
- }
12
- ]
13
- },
14
- {
15
- "matcher": "Bash",
16
- "if": "Bash(*git commit*)",
17
- "hooks": [
18
- {
19
- "type": "command",
20
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/pre-commit-audit-guard.cjs"
21
- }
22
- ]
23
- }
24
- ],
25
- "PostToolUse": [
26
- {
27
- "matcher": "Bash",
28
- "hooks": [
29
- {
30
- "type": "command",
31
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-log.cjs"
32
- }
33
- ]
34
- }
35
- ],
36
- "SessionStart": [
37
- {
38
- "hooks": [
39
- {
40
- "type": "command",
41
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/session-audit.cjs"
42
- }
43
- ]
44
- }
45
- ],
46
- "Stop": [
47
- {
48
- "hooks": [
49
- {
50
- "type": "command",
51
- "command": "node ${CLAUDE_PLUGIN_ROOT}/hooks/stop-quality-check.cjs"
52
- }
53
- ]
54
- }
55
- ]
56
- }
57
- }
1
+ {
2
+ "hooks": {
3
+ "PreToolUse": [
4
+ {
5
+ "matcher": "Bash",
6
+ "if": "Bash(*kastell*)",
7
+ "hooks": [
8
+ {
9
+ "type": "command",
10
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/kastell-plugin/hooks/destroy-block.cjs"
11
+ }
12
+ ]
13
+ },
14
+ {
15
+ "matcher": "Bash",
16
+ "if": "Bash(*git commit*)",
17
+ "hooks": [
18
+ {
19
+ "type": "command",
20
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/kastell-plugin/hooks/pre-commit-audit-guard.cjs"
21
+ }
22
+ ]
23
+ }
24
+ ],
25
+ "PostToolUse": [
26
+ {
27
+ "matcher": "Bash",
28
+ "hooks": [
29
+ {
30
+ "type": "command",
31
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/kastell-plugin/hooks/session-log.cjs"
32
+ }
33
+ ]
34
+ }
35
+ ],
36
+ "SessionStart": [
37
+ {
38
+ "hooks": [
39
+ {
40
+ "type": "command",
41
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/kastell-plugin/hooks/session-audit.cjs"
42
+ }
43
+ ]
44
+ }
45
+ ],
46
+ "Stop": [
47
+ {
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/kastell-plugin/hooks/stop-quality-check.cjs"
52
+ }
53
+ ]
54
+ }
55
+ ]
56
+ }
57
+ }