kastell 2.2.4 → 2.2.6
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/.claude-plugin/marketplace.json +18 -18
- package/.claude-plugin/plugin.json +45 -38
- package/CHANGELOG.md +1313 -1266
- package/LICENSE +201 -201
- package/NOTICE +5 -5
- package/README.md +1 -1
- package/README.tr.md +1 -1
- package/bin/kastell +2 -2
- package/bin/kastell-mcp +5 -5
- package/dist/adapters/coolify.js +92 -92
- package/dist/adapters/dokploy.js +99 -99
- package/dist/commands/fix.d.ts +2 -0
- package/dist/commands/fix.d.ts.map +1 -1
- package/dist/commands/fix.js +26 -1
- package/dist/commands/fix.js.map +1 -1
- package/dist/commands/interactive/plugins.d.ts.map +1 -1
- package/dist/commands/interactive/plugins.js +26 -2
- package/dist/commands/interactive/plugins.js.map +1 -1
- package/dist/commands/plugin.d.ts.map +1 -1
- package/dist/commands/plugin.js +6 -2
- package/dist/commands/plugin.js.map +1 -1
- package/dist/core/audit/commands.d.ts +13 -2
- package/dist/core/audit/commands.d.ts.map +1 -1
- package/dist/core/audit/commands.js +39 -2
- package/dist/core/audit/commands.js.map +1 -1
- package/dist/core/audit/explainCheck.d.ts +1 -0
- package/dist/core/audit/explainCheck.d.ts.map +1 -1
- package/dist/core/audit/explainCheck.js +1 -1
- package/dist/core/audit/explainCheck.js.map +1 -1
- package/dist/core/audit/fix-history.d.ts +3 -1
- package/dist/core/audit/fix-history.d.ts.map +1 -1
- package/dist/core/audit/fix-history.js +6 -2
- package/dist/core/audit/fix-history.js.map +1 -1
- package/dist/core/audit/fix.d.ts.map +1 -1
- package/dist/core/audit/fix.js +22 -0
- package/dist/core/audit/fix.js.map +1 -1
- package/dist/core/audit/formatters/badge.js +20 -20
- package/dist/core/audit/index.d.ts.map +1 -1
- package/dist/core/audit/index.js +12 -3
- package/dist/core/audit/index.js.map +1 -1
- package/dist/core/audit/listChecks.d.ts.map +1 -1
- package/dist/core/audit/listChecks.js +24 -0
- package/dist/core/audit/listChecks.js.map +1 -1
- package/dist/core/audit/pluginAudit.d.ts +8 -0
- package/dist/core/audit/pluginAudit.d.ts.map +1 -0
- package/dist/core/audit/pluginAudit.js +134 -0
- package/dist/core/audit/pluginAudit.js.map +1 -0
- package/dist/core/audit/pluginFix.d.ts +19 -0
- package/dist/core/audit/pluginFix.d.ts.map +1 -0
- package/dist/core/audit/pluginFix.js +122 -0
- package/dist/core/audit/pluginFix.js.map +1 -0
- package/dist/core/audit/snapshot.d.ts +4 -4
- package/dist/core/audit/types.d.ts +2 -1
- package/dist/core/audit/types.d.ts.map +1 -1
- package/dist/core/completions.js +631 -631
- package/dist/core/plugin.d.ts +6 -0
- package/dist/core/plugin.d.ts.map +1 -1
- package/dist/core/plugin.js +2 -0
- package/dist/core/plugin.js.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/pluginTools.d.ts +5 -0
- package/dist/mcp/pluginTools.d.ts.map +1 -0
- package/dist/mcp/pluginTools.js +54 -0
- package/dist/mcp/pluginTools.js.map +1 -0
- package/dist/mcp/prompts/workflows.d.ts +17 -0
- package/dist/mcp/prompts/workflows.d.ts.map +1 -0
- package/dist/mcp/prompts/workflows.js +73 -0
- package/dist/mcp/prompts/workflows.js.map +1 -0
- package/dist/mcp/resources/checks.d.ts +4 -0
- package/dist/mcp/resources/checks.d.ts.map +1 -0
- package/dist/mcp/resources/checks.js +49 -0
- package/dist/mcp/resources/checks.js.map +1 -0
- package/dist/mcp/resources/servers.d.ts +4 -0
- package/dist/mcp/resources/servers.d.ts.map +1 -0
- package/dist/mcp/resources/servers.js +59 -0
- package/dist/mcp/resources/servers.js.map +1 -0
- package/dist/mcp/server.d.ts +1 -1
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +68 -35
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tools/serverAudit.d.ts +1 -1
- package/dist/mcp/tools/serverExplain.d.ts.map +1 -1
- package/dist/mcp/tools/serverExplain.js.map +1 -1
- package/dist/mcp/tools/serverFix.d.ts.map +1 -1
- package/dist/mcp/tools/serverFix.js +7 -1
- package/dist/mcp/tools/serverFix.js.map +1 -1
- package/dist/mcp/tools/serverFleet.d.ts.map +1 -1
- package/dist/mcp/tools/serverFleet.js.map +1 -1
- package/dist/mcp/tools/serverInfo.d.ts +1 -1
- package/dist/mcp/tools/serverInfo.js +1 -1
- package/dist/mcp/tools/serverManage.d.ts +2 -1
- package/dist/mcp/tools/serverManage.d.ts.map +1 -1
- package/dist/mcp/tools/serverManage.js +50 -5
- package/dist/mcp/tools/serverManage.js.map +1 -1
- package/dist/mcp/tools/serverPlugin.d.ts +3 -0
- package/dist/mcp/tools/serverPlugin.d.ts.map +1 -1
- package/dist/mcp/tools/serverPlugin.js +11 -1
- package/dist/mcp/tools/serverPlugin.js.map +1 -1
- package/dist/mcp/tools/serverProvision.d.ts +5 -5
- package/dist/mcp/tools/serverProvision.d.ts.map +1 -1
- package/dist/mcp/tools/serverProvision.js +31 -9
- package/dist/mcp/tools/serverProvision.js.map +1 -1
- package/dist/mcp/tools/serverSecure.d.ts.map +1 -1
- package/dist/mcp/tools/serverSecure.js +30 -1
- package/dist/mcp/tools/serverSecure.js.map +1 -1
- package/dist/mcp/utils.d.ts +25 -0
- package/dist/mcp/utils.d.ts.map +1 -1
- package/dist/mcp/utils.js +36 -0
- package/dist/mcp/utils.js.map +1 -1
- package/dist/mcp-bundle.mjs +102301 -0
- package/dist/plugin/handlerResolver.d.ts +2 -0
- package/dist/plugin/handlerResolver.d.ts.map +1 -0
- package/dist/plugin/handlerResolver.js +16 -0
- package/dist/plugin/handlerResolver.js.map +1 -0
- package/dist/plugin/loader.d.ts.map +1 -1
- package/dist/plugin/loader.js +41 -4
- package/dist/plugin/loader.js.map +1 -1
- package/dist/plugin/registerCommands.d.ts +4 -0
- package/dist/plugin/registerCommands.d.ts.map +1 -0
- package/dist/plugin/registerCommands.js +45 -0
- package/dist/plugin/registerCommands.js.map +1 -0
- package/dist/plugin/registry.d.ts +20 -1
- package/dist/plugin/registry.d.ts.map +1 -1
- package/dist/plugin/registry.js +51 -1
- package/dist/plugin/registry.js.map +1 -1
- package/dist/plugin/sdk/constants.d.ts +2 -0
- package/dist/plugin/sdk/constants.d.ts.map +1 -1
- package/dist/plugin/sdk/constants.js +2 -0
- package/dist/plugin/sdk/constants.js.map +1 -1
- package/dist/plugin/sdk/types.d.ts +74 -1
- package/dist/plugin/sdk/types.d.ts.map +1 -1
- package/dist/plugin/validate.d.ts +2 -1
- package/dist/plugin/validate.d.ts.map +1 -1
- package/dist/plugin/validate.js +106 -1
- package/dist/plugin/validate.js.map +1 -1
- package/dist/utils/cloudInit.js +58 -58
- package/dist/utils/fileLock.d.ts +5 -1
- package/dist/utils/fileLock.d.ts.map +1 -1
- package/dist/utils/fileLock.js +70 -15
- package/dist/utils/fileLock.js.map +1 -1
- package/dist/utils/paths.d.ts +0 -1
- package/dist/utils/paths.d.ts.map +1 -1
- package/dist/utils/paths.js +1 -2
- package/dist/utils/paths.js.map +1 -1
- package/dist/utils/secureWrite.d.ts.map +1 -1
- package/dist/utils/secureWrite.js +3 -38
- package/dist/utils/secureWrite.js.map +1 -1
- package/dist/utils/version.d.ts.map +1 -1
- package/dist/utils/version.js +19 -4
- package/dist/utils/version.js.map +1 -1
- package/kastell-plugin/.claude-plugin/plugin.json +20 -20
- package/kastell-plugin/.mcp.json +15 -8
- package/kastell-plugin/README.md +113 -113
- package/kastell-plugin/agents/kastell-auditor.md +77 -77
- package/kastell-plugin/agents/scripts/bucket_mapper.sh +101 -101
- package/kastell-plugin/agents/scripts/trend_report.sh +91 -91
- package/kastell-plugin/hooks/destroy-block.cjs +31 -31
- package/kastell-plugin/hooks/hooks.json +57 -57
- package/kastell-plugin/hooks/pre-commit-audit-guard.cjs +75 -75
- package/kastell-plugin/hooks/session-audit.cjs +86 -86
- package/kastell-plugin/hooks/session-log.cjs +56 -56
- package/kastell-plugin/hooks/stop-quality-check.cjs +72 -72
- package/kastell-plugin/skills/kastell-careful/SKILL.md +64 -64
- package/kastell-plugin/skills/kastell-ops/SKILL.md +139 -139
- package/kastell-plugin/skills/kastell-ops/references/commands.md +45 -45
- package/kastell-plugin/skills/kastell-ops/references/mcp-tools.md +50 -50
- package/kastell-plugin/skills/kastell-ops/references/patterns.md +145 -145
- package/kastell-plugin/skills/kastell-ops/references/pitfalls.md +136 -136
- package/kastell-plugin/skills/kastell-ops/scripts/check_coverage.sh +101 -101
- package/kastell-plugin/skills/kastell-ops/scripts/fleet_report.sh +73 -73
- package/kastell-plugin/skills/kastell-ops/scripts/parse_audit.sh +76 -76
- package/kastell-plugin/skills/kastell-research/SKILL.md +90 -90
- package/kastell-plugin/skills/kastell-scaffold/SKILL.md +104 -104
- package/kastell-plugin/skills/kastell-scaffold/references/template-audit-check.md +150 -150
- package/kastell-plugin/skills/kastell-scaffold/references/template-command.md +80 -80
- package/kastell-plugin/skills/kastell-scaffold/references/template-mcp-tool.md +72 -72
- package/kastell-plugin/skills/kastell-scaffold/references/template-provider.md +67 -67
- package/kastell-plugin/skills/kastell-scaffold/scripts/scaffold.sh +180 -180
- package/kastell-plugin/skills/kastell-scaffold/templates/check-test.ts.tpl +27 -27
- package/kastell-plugin/skills/kastell-scaffold/templates/check.ts.tpl +50 -50
- package/kastell-plugin/skills/kastell-scaffold/templates/command-core.ts.tpl +18 -18
- package/kastell-plugin/skills/kastell-scaffold/templates/command-test.ts.tpl +17 -17
- package/kastell-plugin/skills/kastell-scaffold/templates/command.ts.tpl +25 -25
- package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool-test.ts.tpl +30 -30
- package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool.ts.tpl +29 -29
- package/kastell-plugin/skills/kastell-scaffold/templates/provider-test.ts.tpl +34 -34
- package/kastell-plugin/skills/kastell-scaffold/templates/provider.ts.tpl +32 -32
- package/package.json +125 -122
- package/dist/commands/interactive.d.ts +0 -11
- package/dist/commands/interactive.d.ts.map +0 -1
- package/dist/commands/interactive.js +0 -1079
- package/dist/commands/interactive.js.map +0 -1
- package/dist/core/lock.d.ts +0 -66
- package/dist/core/lock.d.ts.map +0 -1
- package/dist/core/lock.js +0 -556
- package/dist/core/lock.js.map +0 -1
|
@@ -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
|
+
}
|
|
@@ -1,75 +1,75 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
// PreToolUse hook: Block git commit if audit score dropped since previous audit run
|
|
3
|
-
|
|
4
|
-
const fs = require('fs');
|
|
5
|
-
const path = require('path');
|
|
6
|
-
const os = require('os');
|
|
7
|
-
|
|
8
|
-
const HISTORY_FILE = path.join(os.homedir(), '.kastell', 'audit-history.json');
|
|
9
|
-
|
|
10
|
-
// MANDATORY stdin guard — exit silently if stdin unavailable
|
|
11
|
-
if (!process.stdin || process.stdin.destroyed || !process.stdin.readable) {
|
|
12
|
-
process.exit(0);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
let input = '';
|
|
16
|
-
const stdinTimeout = setTimeout(() => process.exit(0), 1500);
|
|
17
|
-
process.stdin.setEncoding('utf8');
|
|
18
|
-
process.stdin.on('error', () => process.exit(0));
|
|
19
|
-
process.stdin.on('data', chunk => { input += chunk; });
|
|
20
|
-
process.stdin.on('end', () => {
|
|
21
|
-
clearTimeout(stdinTimeout);
|
|
22
|
-
try {
|
|
23
|
-
const cwd = process.cwd();
|
|
24
|
-
|
|
25
|
-
// Kastell project guard (two-phase: directory structure + package.json name)
|
|
26
|
-
const isKastell = fs.existsSync(path.join(cwd, 'src', 'mcp')) &&
|
|
27
|
-
fs.existsSync(path.join(cwd, 'package.json'));
|
|
28
|
-
if (!isKastell) {
|
|
29
|
-
try {
|
|
30
|
-
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
31
|
-
if (pkg.name !== 'kastell') process.exit(0);
|
|
32
|
-
} catch { process.exit(0); }
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// Parse tool input — only act on git commit commands
|
|
36
|
-
const data = JSON.parse(input);
|
|
37
|
-
const cmd = (data.tool_input && data.tool_input.command) || '';
|
|
38
|
-
|
|
39
|
-
if (!/\bgit\s+commit\b/.test(cmd)) {
|
|
40
|
-
process.exit(0);
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Read audit history — fail-open if unavailable
|
|
44
|
-
let history;
|
|
45
|
-
try {
|
|
46
|
-
history = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
47
|
-
} catch {
|
|
48
|
-
// History missing or unreadable — allow commit
|
|
49
|
-
process.exit(0);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
// Check each server for score regression between last two audit runs
|
|
53
|
-
for (const serverIp of Object.keys(history)) {
|
|
54
|
-
const entries = history[serverIp];
|
|
55
|
-
if (!Array.isArray(entries) || entries.length < 2) continue;
|
|
56
|
-
|
|
57
|
-
const current = entries[entries.length - 1].overallScore;
|
|
58
|
-
const previous = entries[entries.length - 2].overallScore;
|
|
59
|
-
|
|
60
|
-
if (typeof current !== 'number' || typeof previous !== 'number') continue;
|
|
61
|
-
if (current < previous) {
|
|
62
|
-
process.stdout.write(JSON.stringify({
|
|
63
|
-
decision: 'block',
|
|
64
|
-
reason: `Audit score dropped: ${previous} -> ${current} (${entries[entries.length - 1].serverName}). Run \`kastell audit\` to investigate before committing.`,
|
|
65
|
-
}));
|
|
66
|
-
process.exit(0);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
// No score drop detected — allow commit
|
|
71
|
-
} catch {}
|
|
72
|
-
|
|
73
|
-
// Fail-open: any unexpected error allows the commit through
|
|
74
|
-
process.exit(0);
|
|
75
|
-
});
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// PreToolUse hook: Block git commit if audit score dropped since previous audit run
|
|
3
|
+
|
|
4
|
+
const fs = require('fs');
|
|
5
|
+
const path = require('path');
|
|
6
|
+
const os = require('os');
|
|
7
|
+
|
|
8
|
+
const HISTORY_FILE = path.join(os.homedir(), '.kastell', 'audit-history.json');
|
|
9
|
+
|
|
10
|
+
// MANDATORY stdin guard — exit silently if stdin unavailable
|
|
11
|
+
if (!process.stdin || process.stdin.destroyed || !process.stdin.readable) {
|
|
12
|
+
process.exit(0);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
let input = '';
|
|
16
|
+
const stdinTimeout = setTimeout(() => process.exit(0), 1500);
|
|
17
|
+
process.stdin.setEncoding('utf8');
|
|
18
|
+
process.stdin.on('error', () => process.exit(0));
|
|
19
|
+
process.stdin.on('data', chunk => { input += chunk; });
|
|
20
|
+
process.stdin.on('end', () => {
|
|
21
|
+
clearTimeout(stdinTimeout);
|
|
22
|
+
try {
|
|
23
|
+
const cwd = process.cwd();
|
|
24
|
+
|
|
25
|
+
// Kastell project guard (two-phase: directory structure + package.json name)
|
|
26
|
+
const isKastell = fs.existsSync(path.join(cwd, 'src', 'mcp')) &&
|
|
27
|
+
fs.existsSync(path.join(cwd, 'package.json'));
|
|
28
|
+
if (!isKastell) {
|
|
29
|
+
try {
|
|
30
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8'));
|
|
31
|
+
if (pkg.name !== 'kastell') process.exit(0);
|
|
32
|
+
} catch { process.exit(0); }
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Parse tool input — only act on git commit commands
|
|
36
|
+
const data = JSON.parse(input);
|
|
37
|
+
const cmd = (data.tool_input && data.tool_input.command) || '';
|
|
38
|
+
|
|
39
|
+
if (!/\bgit\s+commit\b/.test(cmd)) {
|
|
40
|
+
process.exit(0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Read audit history — fail-open if unavailable
|
|
44
|
+
let history;
|
|
45
|
+
try {
|
|
46
|
+
history = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
47
|
+
} catch {
|
|
48
|
+
// History missing or unreadable — allow commit
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Check each server for score regression between last two audit runs
|
|
53
|
+
for (const serverIp of Object.keys(history)) {
|
|
54
|
+
const entries = history[serverIp];
|
|
55
|
+
if (!Array.isArray(entries) || entries.length < 2) continue;
|
|
56
|
+
|
|
57
|
+
const current = entries[entries.length - 1].overallScore;
|
|
58
|
+
const previous = entries[entries.length - 2].overallScore;
|
|
59
|
+
|
|
60
|
+
if (typeof current !== 'number' || typeof previous !== 'number') continue;
|
|
61
|
+
if (current < previous) {
|
|
62
|
+
process.stdout.write(JSON.stringify({
|
|
63
|
+
decision: 'block',
|
|
64
|
+
reason: `Audit score dropped: ${previous} -> ${current} (${entries[entries.length - 1].serverName}). Run \`kastell audit\` to investigate before committing.`,
|
|
65
|
+
}));
|
|
66
|
+
process.exit(0);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// No score drop detected — allow commit
|
|
71
|
+
} catch {}
|
|
72
|
+
|
|
73
|
+
// Fail-open: any unexpected error allows the commit through
|
|
74
|
+
process.exit(0);
|
|
75
|
+
});
|