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.
Files changed (199) hide show
  1. package/.claude-plugin/marketplace.json +18 -18
  2. package/.claude-plugin/plugin.json +45 -38
  3. package/CHANGELOG.md +1313 -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/commands/fix.d.ts +2 -0
  13. package/dist/commands/fix.d.ts.map +1 -1
  14. package/dist/commands/fix.js +26 -1
  15. package/dist/commands/fix.js.map +1 -1
  16. package/dist/commands/interactive/plugins.d.ts.map +1 -1
  17. package/dist/commands/interactive/plugins.js +26 -2
  18. package/dist/commands/interactive/plugins.js.map +1 -1
  19. package/dist/commands/plugin.d.ts.map +1 -1
  20. package/dist/commands/plugin.js +6 -2
  21. package/dist/commands/plugin.js.map +1 -1
  22. package/dist/core/audit/commands.d.ts +13 -2
  23. package/dist/core/audit/commands.d.ts.map +1 -1
  24. package/dist/core/audit/commands.js +39 -2
  25. package/dist/core/audit/commands.js.map +1 -1
  26. package/dist/core/audit/explainCheck.d.ts +1 -0
  27. package/dist/core/audit/explainCheck.d.ts.map +1 -1
  28. package/dist/core/audit/explainCheck.js +1 -1
  29. package/dist/core/audit/explainCheck.js.map +1 -1
  30. package/dist/core/audit/fix-history.d.ts +3 -1
  31. package/dist/core/audit/fix-history.d.ts.map +1 -1
  32. package/dist/core/audit/fix-history.js +6 -2
  33. package/dist/core/audit/fix-history.js.map +1 -1
  34. package/dist/core/audit/fix.d.ts.map +1 -1
  35. package/dist/core/audit/fix.js +22 -0
  36. package/dist/core/audit/fix.js.map +1 -1
  37. package/dist/core/audit/formatters/badge.js +20 -20
  38. package/dist/core/audit/index.d.ts.map +1 -1
  39. package/dist/core/audit/index.js +12 -3
  40. package/dist/core/audit/index.js.map +1 -1
  41. package/dist/core/audit/listChecks.d.ts.map +1 -1
  42. package/dist/core/audit/listChecks.js +24 -0
  43. package/dist/core/audit/listChecks.js.map +1 -1
  44. package/dist/core/audit/pluginAudit.d.ts +8 -0
  45. package/dist/core/audit/pluginAudit.d.ts.map +1 -0
  46. package/dist/core/audit/pluginAudit.js +134 -0
  47. package/dist/core/audit/pluginAudit.js.map +1 -0
  48. package/dist/core/audit/pluginFix.d.ts +19 -0
  49. package/dist/core/audit/pluginFix.d.ts.map +1 -0
  50. package/dist/core/audit/pluginFix.js +122 -0
  51. package/dist/core/audit/pluginFix.js.map +1 -0
  52. package/dist/core/audit/snapshot.d.ts +4 -4
  53. package/dist/core/audit/types.d.ts +2 -1
  54. package/dist/core/audit/types.d.ts.map +1 -1
  55. package/dist/core/completions.js +631 -631
  56. package/dist/core/plugin.d.ts +6 -0
  57. package/dist/core/plugin.d.ts.map +1 -1
  58. package/dist/core/plugin.js +2 -0
  59. package/dist/core/plugin.js.map +1 -1
  60. package/dist/index.js +7 -0
  61. package/dist/index.js.map +1 -1
  62. package/dist/mcp/index.js +1 -1
  63. package/dist/mcp/index.js.map +1 -1
  64. package/dist/mcp/pluginTools.d.ts +5 -0
  65. package/dist/mcp/pluginTools.d.ts.map +1 -0
  66. package/dist/mcp/pluginTools.js +54 -0
  67. package/dist/mcp/pluginTools.js.map +1 -0
  68. package/dist/mcp/prompts/workflows.d.ts +17 -0
  69. package/dist/mcp/prompts/workflows.d.ts.map +1 -0
  70. package/dist/mcp/prompts/workflows.js +73 -0
  71. package/dist/mcp/prompts/workflows.js.map +1 -0
  72. package/dist/mcp/resources/checks.d.ts +4 -0
  73. package/dist/mcp/resources/checks.d.ts.map +1 -0
  74. package/dist/mcp/resources/checks.js +49 -0
  75. package/dist/mcp/resources/checks.js.map +1 -0
  76. package/dist/mcp/resources/servers.d.ts +4 -0
  77. package/dist/mcp/resources/servers.d.ts.map +1 -0
  78. package/dist/mcp/resources/servers.js +59 -0
  79. package/dist/mcp/resources/servers.js.map +1 -0
  80. package/dist/mcp/server.d.ts +1 -1
  81. package/dist/mcp/server.d.ts.map +1 -1
  82. package/dist/mcp/server.js +68 -35
  83. package/dist/mcp/server.js.map +1 -1
  84. package/dist/mcp/tools/serverAudit.d.ts +1 -1
  85. package/dist/mcp/tools/serverExplain.d.ts.map +1 -1
  86. package/dist/mcp/tools/serverExplain.js.map +1 -1
  87. package/dist/mcp/tools/serverFix.d.ts.map +1 -1
  88. package/dist/mcp/tools/serverFix.js +7 -1
  89. package/dist/mcp/tools/serverFix.js.map +1 -1
  90. package/dist/mcp/tools/serverFleet.d.ts.map +1 -1
  91. package/dist/mcp/tools/serverFleet.js.map +1 -1
  92. package/dist/mcp/tools/serverInfo.d.ts +1 -1
  93. package/dist/mcp/tools/serverInfo.js +1 -1
  94. package/dist/mcp/tools/serverManage.d.ts +2 -1
  95. package/dist/mcp/tools/serverManage.d.ts.map +1 -1
  96. package/dist/mcp/tools/serverManage.js +50 -5
  97. package/dist/mcp/tools/serverManage.js.map +1 -1
  98. package/dist/mcp/tools/serverPlugin.d.ts +3 -0
  99. package/dist/mcp/tools/serverPlugin.d.ts.map +1 -1
  100. package/dist/mcp/tools/serverPlugin.js +11 -1
  101. package/dist/mcp/tools/serverPlugin.js.map +1 -1
  102. package/dist/mcp/tools/serverProvision.d.ts +5 -5
  103. package/dist/mcp/tools/serverProvision.d.ts.map +1 -1
  104. package/dist/mcp/tools/serverProvision.js +31 -9
  105. package/dist/mcp/tools/serverProvision.js.map +1 -1
  106. package/dist/mcp/tools/serverSecure.d.ts.map +1 -1
  107. package/dist/mcp/tools/serverSecure.js +30 -1
  108. package/dist/mcp/tools/serverSecure.js.map +1 -1
  109. package/dist/mcp/utils.d.ts +25 -0
  110. package/dist/mcp/utils.d.ts.map +1 -1
  111. package/dist/mcp/utils.js +36 -0
  112. package/dist/mcp/utils.js.map +1 -1
  113. package/dist/mcp-bundle.mjs +102301 -0
  114. package/dist/plugin/handlerResolver.d.ts +2 -0
  115. package/dist/plugin/handlerResolver.d.ts.map +1 -0
  116. package/dist/plugin/handlerResolver.js +16 -0
  117. package/dist/plugin/handlerResolver.js.map +1 -0
  118. package/dist/plugin/loader.d.ts.map +1 -1
  119. package/dist/plugin/loader.js +41 -4
  120. package/dist/plugin/loader.js.map +1 -1
  121. package/dist/plugin/registerCommands.d.ts +4 -0
  122. package/dist/plugin/registerCommands.d.ts.map +1 -0
  123. package/dist/plugin/registerCommands.js +45 -0
  124. package/dist/plugin/registerCommands.js.map +1 -0
  125. package/dist/plugin/registry.d.ts +20 -1
  126. package/dist/plugin/registry.d.ts.map +1 -1
  127. package/dist/plugin/registry.js +51 -1
  128. package/dist/plugin/registry.js.map +1 -1
  129. package/dist/plugin/sdk/constants.d.ts +2 -0
  130. package/dist/plugin/sdk/constants.d.ts.map +1 -1
  131. package/dist/plugin/sdk/constants.js +2 -0
  132. package/dist/plugin/sdk/constants.js.map +1 -1
  133. package/dist/plugin/sdk/types.d.ts +74 -1
  134. package/dist/plugin/sdk/types.d.ts.map +1 -1
  135. package/dist/plugin/validate.d.ts +2 -1
  136. package/dist/plugin/validate.d.ts.map +1 -1
  137. package/dist/plugin/validate.js +106 -1
  138. package/dist/plugin/validate.js.map +1 -1
  139. package/dist/utils/cloudInit.js +58 -58
  140. package/dist/utils/fileLock.d.ts +5 -1
  141. package/dist/utils/fileLock.d.ts.map +1 -1
  142. package/dist/utils/fileLock.js +70 -15
  143. package/dist/utils/fileLock.js.map +1 -1
  144. package/dist/utils/paths.d.ts +0 -1
  145. package/dist/utils/paths.d.ts.map +1 -1
  146. package/dist/utils/paths.js +1 -2
  147. package/dist/utils/paths.js.map +1 -1
  148. package/dist/utils/secureWrite.d.ts.map +1 -1
  149. package/dist/utils/secureWrite.js +3 -38
  150. package/dist/utils/secureWrite.js.map +1 -1
  151. package/dist/utils/version.d.ts.map +1 -1
  152. package/dist/utils/version.js +19 -4
  153. package/dist/utils/version.js.map +1 -1
  154. package/kastell-plugin/.claude-plugin/plugin.json +20 -20
  155. package/kastell-plugin/.mcp.json +15 -8
  156. package/kastell-plugin/README.md +113 -113
  157. package/kastell-plugin/agents/kastell-auditor.md +77 -77
  158. package/kastell-plugin/agents/scripts/bucket_mapper.sh +101 -101
  159. package/kastell-plugin/agents/scripts/trend_report.sh +91 -91
  160. package/kastell-plugin/hooks/destroy-block.cjs +31 -31
  161. package/kastell-plugin/hooks/hooks.json +57 -57
  162. package/kastell-plugin/hooks/pre-commit-audit-guard.cjs +75 -75
  163. package/kastell-plugin/hooks/session-audit.cjs +86 -86
  164. package/kastell-plugin/hooks/session-log.cjs +56 -56
  165. package/kastell-plugin/hooks/stop-quality-check.cjs +72 -72
  166. package/kastell-plugin/skills/kastell-careful/SKILL.md +64 -64
  167. package/kastell-plugin/skills/kastell-ops/SKILL.md +139 -139
  168. package/kastell-plugin/skills/kastell-ops/references/commands.md +45 -45
  169. package/kastell-plugin/skills/kastell-ops/references/mcp-tools.md +50 -50
  170. package/kastell-plugin/skills/kastell-ops/references/patterns.md +145 -145
  171. package/kastell-plugin/skills/kastell-ops/references/pitfalls.md +136 -136
  172. package/kastell-plugin/skills/kastell-ops/scripts/check_coverage.sh +101 -101
  173. package/kastell-plugin/skills/kastell-ops/scripts/fleet_report.sh +73 -73
  174. package/kastell-plugin/skills/kastell-ops/scripts/parse_audit.sh +76 -76
  175. package/kastell-plugin/skills/kastell-research/SKILL.md +90 -90
  176. package/kastell-plugin/skills/kastell-scaffold/SKILL.md +104 -104
  177. package/kastell-plugin/skills/kastell-scaffold/references/template-audit-check.md +150 -150
  178. package/kastell-plugin/skills/kastell-scaffold/references/template-command.md +80 -80
  179. package/kastell-plugin/skills/kastell-scaffold/references/template-mcp-tool.md +72 -72
  180. package/kastell-plugin/skills/kastell-scaffold/references/template-provider.md +67 -67
  181. package/kastell-plugin/skills/kastell-scaffold/scripts/scaffold.sh +180 -180
  182. package/kastell-plugin/skills/kastell-scaffold/templates/check-test.ts.tpl +27 -27
  183. package/kastell-plugin/skills/kastell-scaffold/templates/check.ts.tpl +50 -50
  184. package/kastell-plugin/skills/kastell-scaffold/templates/command-core.ts.tpl +18 -18
  185. package/kastell-plugin/skills/kastell-scaffold/templates/command-test.ts.tpl +17 -17
  186. package/kastell-plugin/skills/kastell-scaffold/templates/command.ts.tpl +25 -25
  187. package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool-test.ts.tpl +30 -30
  188. package/kastell-plugin/skills/kastell-scaffold/templates/mcp-tool.ts.tpl +29 -29
  189. package/kastell-plugin/skills/kastell-scaffold/templates/provider-test.ts.tpl +34 -34
  190. package/kastell-plugin/skills/kastell-scaffold/templates/provider.ts.tpl +32 -32
  191. package/package.json +125 -122
  192. package/dist/commands/interactive.d.ts +0 -11
  193. package/dist/commands/interactive.d.ts.map +0 -1
  194. package/dist/commands/interactive.js +0 -1079
  195. package/dist/commands/interactive.js.map +0 -1
  196. package/dist/core/lock.d.ts +0 -66
  197. package/dist/core/lock.d.ts.map +0 -1
  198. package/dist/core/lock.js +0 -556
  199. 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
+ });