@eddacraft/anvil-runtime 0.1.0

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 (170) hide show
  1. package/LICENSE +14 -0
  2. package/dist/cache/cache-key.d.ts +45 -0
  3. package/dist/cache/cache-key.d.ts.map +1 -0
  4. package/dist/cache/cache-key.js +135 -0
  5. package/dist/cache/index.d.ts +27 -0
  6. package/dist/cache/index.d.ts.map +1 -0
  7. package/dist/cache/index.js +38 -0
  8. package/dist/cache/providers/file-cache.d.ts +63 -0
  9. package/dist/cache/providers/file-cache.d.ts.map +1 -0
  10. package/dist/cache/providers/file-cache.js +369 -0
  11. package/dist/cache/providers/memory-cache.d.ts +52 -0
  12. package/dist/cache/providers/memory-cache.d.ts.map +1 -0
  13. package/dist/cache/providers/memory-cache.js +197 -0
  14. package/dist/cache/providers/null-cache.d.ts +26 -0
  15. package/dist/cache/providers/null-cache.d.ts.map +1 -0
  16. package/dist/cache/providers/null-cache.js +50 -0
  17. package/dist/cache/types.d.ts +114 -0
  18. package/dist/cache/types.d.ts.map +1 -0
  19. package/dist/cache/types.js +4 -0
  20. package/dist/concurrency/agent.d.ts +137 -0
  21. package/dist/concurrency/agent.d.ts.map +1 -0
  22. package/dist/concurrency/agent.js +440 -0
  23. package/dist/concurrency/atomic.d.ts +93 -0
  24. package/dist/concurrency/atomic.d.ts.map +1 -0
  25. package/dist/concurrency/atomic.js +281 -0
  26. package/dist/concurrency/git-agent.d.ts +114 -0
  27. package/dist/concurrency/git-agent.d.ts.map +1 -0
  28. package/dist/concurrency/git-agent.js +313 -0
  29. package/dist/concurrency/index.d.ts +95 -0
  30. package/dist/concurrency/index.d.ts.map +1 -0
  31. package/dist/concurrency/index.js +127 -0
  32. package/dist/concurrency/lock-manager.d.ts +170 -0
  33. package/dist/concurrency/lock-manager.d.ts.map +1 -0
  34. package/dist/concurrency/lock-manager.js +525 -0
  35. package/dist/concurrency/queue-manager.d.ts +166 -0
  36. package/dist/concurrency/queue-manager.d.ts.map +1 -0
  37. package/dist/concurrency/queue-manager.js +442 -0
  38. package/dist/concurrency/types.d.ts +382 -0
  39. package/dist/concurrency/types.d.ts.map +1 -0
  40. package/dist/concurrency/types.js +204 -0
  41. package/dist/export/constraint-collector.d.ts +175 -0
  42. package/dist/export/constraint-collector.d.ts.map +1 -0
  43. package/dist/export/constraint-collector.js +203 -0
  44. package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
  45. package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
  46. package/dist/export/formatters/llms-txt-formatter.js +249 -0
  47. package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
  48. package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
  49. package/dist/export/formatters/mcp-resource-formatter.js +139 -0
  50. package/dist/export/formatters/prompt-formatter.d.ts +83 -0
  51. package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
  52. package/dist/export/formatters/prompt-formatter.js +256 -0
  53. package/dist/export/index.d.ts +10 -0
  54. package/dist/export/index.d.ts.map +1 -0
  55. package/dist/export/index.js +9 -0
  56. package/dist/gate/check.interface.d.ts +15 -0
  57. package/dist/gate/check.interface.d.ts.map +1 -0
  58. package/dist/gate/check.interface.js +18 -0
  59. package/dist/gate/checks/antipattern.check.d.ts +27 -0
  60. package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
  61. package/dist/gate/checks/antipattern.check.js +140 -0
  62. package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
  63. package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
  64. package/dist/gate/checks/architecture/circular-detector.js +71 -0
  65. package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
  66. package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
  67. package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
  68. package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
  69. package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
  70. package/dist/gate/checks/architecture/layer-validator.js +193 -0
  71. package/dist/gate/checks/architecture.check.d.ts +56 -0
  72. package/dist/gate/checks/architecture.check.d.ts.map +1 -0
  73. package/dist/gate/checks/architecture.check.js +394 -0
  74. package/dist/gate/checks/command-safety.check.d.ts +12 -0
  75. package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
  76. package/dist/gate/checks/command-safety.check.js +230 -0
  77. package/dist/gate/checks/coverage.check.d.ts +9 -0
  78. package/dist/gate/checks/coverage.check.d.ts.map +1 -0
  79. package/dist/gate/checks/coverage.check.js +81 -0
  80. package/dist/gate/checks/dependency.check.d.ts +17 -0
  81. package/dist/gate/checks/dependency.check.d.ts.map +1 -0
  82. package/dist/gate/checks/dependency.check.js +342 -0
  83. package/dist/gate/checks/eslint.check.d.ts +14 -0
  84. package/dist/gate/checks/eslint.check.d.ts.map +1 -0
  85. package/dist/gate/checks/eslint.check.js +79 -0
  86. package/dist/gate/checks/policy.check.d.ts +78 -0
  87. package/dist/gate/checks/policy.check.d.ts.map +1 -0
  88. package/dist/gate/checks/policy.check.js +457 -0
  89. package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
  90. package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
  91. package/dist/gate/checks/secret/entropy-detector.js +76 -0
  92. package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
  93. package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
  94. package/dist/gate/checks/secret/git-scanner.js +90 -0
  95. package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
  96. package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
  97. package/dist/gate/checks/secret/secret-patterns.js +137 -0
  98. package/dist/gate/checks/secret.check.d.ts +56 -0
  99. package/dist/gate/checks/secret.check.d.ts.map +1 -0
  100. package/dist/gate/checks/secret.check.js +245 -0
  101. package/dist/gate/config/command-safety-config.d.ts +5 -0
  102. package/dist/gate/config/command-safety-config.d.ts.map +1 -0
  103. package/dist/gate/config/command-safety-config.js +69 -0
  104. package/dist/gate/config/index.d.ts +2 -0
  105. package/dist/gate/config/index.d.ts.map +1 -0
  106. package/dist/gate/config/index.js +1 -0
  107. package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
  108. package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
  109. package/dist/gate/formatters/command-safety-formatter.js +64 -0
  110. package/dist/gate/formatters/index.d.ts +2 -0
  111. package/dist/gate/formatters/index.d.ts.map +1 -0
  112. package/dist/gate/formatters/index.js +1 -0
  113. package/dist/gate/gate-config.d.ts +44 -0
  114. package/dist/gate/gate-config.d.ts.map +1 -0
  115. package/dist/gate/gate-config.js +334 -0
  116. package/dist/gate/gate-runner.d.ts +160 -0
  117. package/dist/gate/gate-runner.d.ts.map +1 -0
  118. package/dist/gate/gate-runner.js +531 -0
  119. package/dist/gate/index.d.ts +20 -0
  120. package/dist/gate/index.d.ts.map +1 -0
  121. package/dist/gate/index.js +14 -0
  122. package/dist/gate/parsers/command-parser.d.ts +18 -0
  123. package/dist/gate/parsers/command-parser.d.ts.map +1 -0
  124. package/dist/gate/parsers/command-parser.js +363 -0
  125. package/dist/gate/parsers/index.d.ts +2 -0
  126. package/dist/gate/parsers/index.d.ts.map +1 -0
  127. package/dist/gate/parsers/index.js +1 -0
  128. package/dist/gate/policy/index.d.ts +12 -0
  129. package/dist/gate/policy/index.d.ts.map +1 -0
  130. package/dist/gate/policy/index.js +10 -0
  131. package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
  132. package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
  133. package/dist/gate/rules/default-filesystem-rules.js +201 -0
  134. package/dist/gate/rules/default-git-rules.d.ts +3 -0
  135. package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
  136. package/dist/gate/rules/default-git-rules.js +192 -0
  137. package/dist/gate/rules/index.d.ts +5 -0
  138. package/dist/gate/rules/index.d.ts.map +1 -0
  139. package/dist/gate/rules/index.js +3 -0
  140. package/dist/gate/rules/rule-matcher.d.ts +27 -0
  141. package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
  142. package/dist/gate/rules/rule-matcher.js +228 -0
  143. package/dist/gate/rules/types.d.ts +250 -0
  144. package/dist/gate/rules/types.d.ts.map +1 -0
  145. package/dist/gate/rules/types.js +1 -0
  146. package/dist/index.d.ts +19 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +35 -0
  149. package/dist/types/gate.types.d.ts +42 -0
  150. package/dist/types/gate.types.d.ts.map +1 -0
  151. package/dist/types/gate.types.js +94 -0
  152. package/dist/watch/debouncer.d.ts +90 -0
  153. package/dist/watch/debouncer.d.ts.map +1 -0
  154. package/dist/watch/debouncer.js +135 -0
  155. package/dist/watch/file-watcher.d.ts +73 -0
  156. package/dist/watch/file-watcher.d.ts.map +1 -0
  157. package/dist/watch/file-watcher.js +121 -0
  158. package/dist/watch/git-status.d.ts +98 -0
  159. package/dist/watch/git-status.d.ts.map +1 -0
  160. package/dist/watch/git-status.js +266 -0
  161. package/dist/watch/index.d.ts +16 -0
  162. package/dist/watch/index.d.ts.map +1 -0
  163. package/dist/watch/index.js +15 -0
  164. package/dist/watch/orchestrator.d.ts +113 -0
  165. package/dist/watch/orchestrator.d.ts.map +1 -0
  166. package/dist/watch/orchestrator.js +409 -0
  167. package/dist/watch/types.d.ts +190 -0
  168. package/dist/watch/types.d.ts.map +1 -0
  169. package/dist/watch/types.js +76 -0
  170. package/package.json +60 -0
@@ -0,0 +1,192 @@
1
+ export const DEFAULT_GIT_RULES = [
2
+ {
3
+ id: 'git-reset-hard',
4
+ category: 'git',
5
+ command: 'git',
6
+ subcommand: 'reset',
7
+ flags: { dangerous: ['--hard'] },
8
+ action: 'block',
9
+ severity: 'error',
10
+ reason: 'git reset --hard permanently destroys uncommitted changes',
11
+ suggestion: 'Use "git stash" first to preserve your work, or "git reset --soft" for a safer alternative',
12
+ references: [
13
+ 'https://git-scm.com/docs/git-reset',
14
+ 'https://ohshitgit.com/#accidental-commit-wrong-branch',
15
+ ],
16
+ },
17
+ {
18
+ id: 'git-reset-merge',
19
+ category: 'git',
20
+ command: 'git',
21
+ subcommand: 'reset',
22
+ flags: { dangerous: ['--merge'] },
23
+ action: 'warn',
24
+ severity: 'warning',
25
+ reason: 'git reset --merge can lose uncommitted changes during conflict resolution',
26
+ suggestion: 'Ensure all changes are committed or stashed before using --merge',
27
+ },
28
+ {
29
+ id: 'git-checkout-discard',
30
+ category: 'git',
31
+ command: 'git',
32
+ subcommand: 'checkout',
33
+ flags: { dangerous: ['--'] },
34
+ action: 'block',
35
+ severity: 'error',
36
+ reason: 'git checkout -- discards uncommitted changes permanently',
37
+ suggestion: 'Use "git stash" to preserve changes, or "git diff" to review first',
38
+ references: ['https://git-scm.com/docs/git-checkout'],
39
+ },
40
+ {
41
+ id: 'git-checkout-all',
42
+ category: 'git',
43
+ command: 'git',
44
+ subcommand: 'checkout',
45
+ args: { pattern: /^\.$/ },
46
+ action: 'warn',
47
+ severity: 'warning',
48
+ reason: 'git checkout . discards all uncommitted changes in the working tree',
49
+ suggestion: 'Use "git stash" to preserve changes, or "git diff" to review first',
50
+ },
51
+ {
52
+ id: 'git-restore-worktree',
53
+ category: 'git',
54
+ command: 'git',
55
+ subcommand: 'restore',
56
+ flags: { forbidden: ['--staged'] },
57
+ action: 'block',
58
+ severity: 'error',
59
+ reason: 'git restore discards uncommitted changes permanently',
60
+ suggestion: 'Use "git stash" first, or "git restore --staged" to only unstage',
61
+ },
62
+ {
63
+ id: 'git-clean-force',
64
+ category: 'git',
65
+ command: 'git',
66
+ subcommand: 'clean',
67
+ flags: { dangerous: ['-f', '--force'], forbidden: ['-n', '--dry-run'] },
68
+ action: 'warn',
69
+ severity: 'warning',
70
+ reason: 'git clean -f permanently removes untracked files',
71
+ suggestion: 'Preview with "git clean -n" (dry-run) first',
72
+ },
73
+ {
74
+ id: 'git-push-force',
75
+ category: 'git',
76
+ command: 'git',
77
+ subcommand: 'push',
78
+ flags: { dangerous: ['-f', '--force'], forbidden: ['--force-with-lease'] },
79
+ action: 'block',
80
+ severity: 'error',
81
+ reason: 'git push --force rewrites remote history and can cause data loss for collaborators',
82
+ suggestion: 'Use "git push --force-with-lease" for safer force pushing, or coordinate with your team',
83
+ references: [
84
+ 'https://git-scm.com/docs/git-push#Documentation/git-push.txt---force-with-leaseltrefnamegt',
85
+ ],
86
+ },
87
+ {
88
+ id: 'git-branch-force-delete',
89
+ category: 'git',
90
+ command: 'git',
91
+ subcommand: 'branch',
92
+ flags: { dangerous: ['-D'] },
93
+ action: 'warn',
94
+ severity: 'warning',
95
+ reason: 'git branch -D force-deletes branches without merge verification',
96
+ suggestion: 'Use "git branch -d" for safe deletion with merge checks',
97
+ },
98
+ {
99
+ id: 'git-stash-drop',
100
+ category: 'git',
101
+ command: 'git',
102
+ subcommand: 'stash',
103
+ args: { pattern: /^drop$/, position: 0 },
104
+ action: 'warn',
105
+ severity: 'warning',
106
+ reason: 'git stash drop permanently deletes stashed changes',
107
+ suggestion: 'Review stashed changes with "git stash show -p" before dropping',
108
+ },
109
+ {
110
+ id: 'git-stash-clear',
111
+ category: 'git',
112
+ command: 'git',
113
+ subcommand: 'stash',
114
+ args: { pattern: /^clear$/, position: 0 },
115
+ action: 'warn',
116
+ severity: 'warning',
117
+ reason: 'git stash clear permanently deletes all stashed changes',
118
+ suggestion: 'Review stashes with "git stash list" before clearing',
119
+ },
120
+ {
121
+ id: 'git-rebase-abort',
122
+ category: 'git',
123
+ command: 'git',
124
+ subcommand: 'rebase',
125
+ flags: { dangerous: ['--abort'] },
126
+ action: 'warn',
127
+ severity: 'warning',
128
+ reason: 'git rebase --abort discards rebase progress',
129
+ suggestion: 'Ensure you want to discard all rebase progress before aborting',
130
+ },
131
+ {
132
+ id: 'git-merge-abort',
133
+ category: 'git',
134
+ command: 'git',
135
+ subcommand: 'merge',
136
+ flags: { dangerous: ['--abort'] },
137
+ action: 'warn',
138
+ severity: 'warning',
139
+ reason: 'git merge --abort discards merge progress',
140
+ suggestion: 'Ensure you want to discard all merge progress before aborting',
141
+ },
142
+ {
143
+ id: 'git-checkout-branch',
144
+ category: 'git',
145
+ command: 'git',
146
+ subcommand: 'checkout',
147
+ flags: { required: ['-b', '-B', '--orphan'] },
148
+ action: 'allow',
149
+ severity: 'info',
150
+ reason: 'Branch creation is a safe operation',
151
+ },
152
+ {
153
+ id: 'git-restore-staged',
154
+ category: 'git',
155
+ command: 'git',
156
+ subcommand: 'restore',
157
+ flags: { required: ['--staged'] },
158
+ action: 'allow',
159
+ severity: 'info',
160
+ reason: 'Unstaging changes is a safe operation',
161
+ },
162
+ {
163
+ id: 'git-push-force-with-lease',
164
+ category: 'git',
165
+ command: 'git',
166
+ subcommand: 'push',
167
+ flags: { required: ['--force-with-lease'] },
168
+ action: 'allow',
169
+ severity: 'info',
170
+ reason: 'Force-with-lease is a safer alternative to --force',
171
+ },
172
+ {
173
+ id: 'git-branch-safe-delete',
174
+ category: 'git',
175
+ command: 'git',
176
+ subcommand: 'branch',
177
+ flags: { required: ['-d'], forbidden: ['-D'] },
178
+ action: 'allow',
179
+ severity: 'info',
180
+ reason: 'Safe branch deletion with merge verification',
181
+ },
182
+ {
183
+ id: 'git-clean-dry-run',
184
+ category: 'git',
185
+ command: 'git',
186
+ subcommand: 'clean',
187
+ flags: { required: ['-n', '--dry-run'] },
188
+ action: 'allow',
189
+ severity: 'info',
190
+ reason: 'Dry-run preview is safe',
191
+ },
192
+ ];
@@ -0,0 +1,5 @@
1
+ export type { CommandCategory, CommandAction, CommandSeverity, CommandFlagConfig, CommandArgConfig, CommandConditions, CommandRule, CommandRuleset, ParsedCommand, CommandAnalysisResult, CommandAnalysisSummary, CommandRuleOverride, CommandRulesConfig, WorkingDirectoryConfig, CommandSafetyOutputConfig, CommandSafetyConfig, ResolvedCommandSafetyConfig, CommandSafetyFinding, CommandSafetyDetails, } from './types.js';
2
+ export { findMatchingRule, analyseCommand, calculateSpecificity, RuleMatcher, } from './rule-matcher.js';
3
+ export { DEFAULT_GIT_RULES } from './default-git-rules.js';
4
+ export { DEFAULT_FILESYSTEM_RULES } from './default-filesystem-rules.js';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/gate/rules/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,cAAc,EACd,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACtB,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EACtB,yBAAyB,EACzB,mBAAmB,EACnB,2BAA2B,EAC3B,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAEpB,OAAO,EACL,gBAAgB,EAChB,cAAc,EACd,oBAAoB,EACpB,WAAW,GACZ,MAAM,mBAAmB,CAAC;AAE3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { findMatchingRule, analyseCommand, calculateSpecificity, RuleMatcher, } from './rule-matcher.js';
2
+ export { DEFAULT_GIT_RULES } from './default-git-rules.js';
3
+ export { DEFAULT_FILESYSTEM_RULES } from './default-filesystem-rules.js';
@@ -0,0 +1,27 @@
1
+ import type { CommandRule, ParsedCommand, CommandAnalysisResult, WorkingDirectoryConfig } from './types.js';
2
+ export interface MatcherContext {
3
+ strict?: boolean;
4
+ workingDirectory?: WorkingDirectoryConfig;
5
+ cwd?: string;
6
+ }
7
+ declare function calculateSpecificity(rule: CommandRule): number;
8
+ export declare function findMatchingRule(parsed: ParsedCommand, rules: CommandRule[], context?: MatcherContext): CommandRule | undefined;
9
+ export declare function analyseCommand(command: string, parsed: ParsedCommand, rules: CommandRule[], context?: MatcherContext): CommandAnalysisResult;
10
+ export declare class RuleMatcher {
11
+ private rules;
12
+ private context?;
13
+ constructor(rules?: CommandRule[], context?: MatcherContext);
14
+ setContext(context: MatcherContext): void;
15
+ addRule(rule: CommandRule): void;
16
+ addRules(rules: CommandRule[]): void;
17
+ setRules(rules: CommandRule[]): void;
18
+ getRules(): CommandRule[];
19
+ findMatchingRule(parsed: ParsedCommand, context?: MatcherContext): CommandRule | undefined;
20
+ analyse(command: string, parsed: ParsedCommand, context?: MatcherContext): CommandAnalysisResult;
21
+ analyseMultiple(commands: Array<{
22
+ command: string;
23
+ parsed: ParsedCommand;
24
+ }>, context?: MatcherContext): CommandAnalysisResult[];
25
+ }
26
+ export { calculateSpecificity };
27
+ //# sourceMappingURL=rule-matcher.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rule-matcher.d.ts","sourceRoot":"","sources":["../../../src/gate/rules/rule-matcher.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,aAAa,EACb,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,YAAY,CAAC;AAKpB,MAAM,WAAW,cAAc;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,gBAAgB,CAAC,EAAE,sBAAsB,CAAC;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAOD,iBAAS,oBAAoB,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,CAuBvD;AA0KD,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,WAAW,GAAG,SAAS,CAmBzB;AAED,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,aAAa,EACrB,KAAK,EAAE,WAAW,EAAE,EACpB,OAAO,CAAC,EAAE,cAAc,GACvB,qBAAqB,CA2BvB;AAED,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAgB;IAC7B,OAAO,CAAC,OAAO,CAAC,CAAiB;gBAErB,KAAK,GAAE,WAAW,EAAO,EAAE,OAAO,CAAC,EAAE,cAAc;IAK/D,UAAU,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI;IAIzC,OAAO,CAAC,IAAI,EAAE,WAAW,GAAG,IAAI;IAIhC,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;IAIpC,QAAQ,CAAC,KAAK,EAAE,WAAW,EAAE,GAAG,IAAI;IAIpC,QAAQ,IAAI,WAAW,EAAE;IAIzB,gBAAgB,CAAC,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,WAAW,GAAG,SAAS;IAI1F,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,CAAC,EAAE,cAAc,GAAG,qBAAqB;IAIhG,eAAe,CACb,QAAQ,EAAE,KAAK,CAAC;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,aAAa,CAAA;KAAE,CAAC,EAC3D,OAAO,CAAC,EAAE,cAAc,GACvB,qBAAqB,EAAE;CAI3B;AAED,OAAO,EAAE,oBAAoB,EAAE,CAAC"}
@@ -0,0 +1,228 @@
1
+ import { createDebugger } from '@eddacraft/anvil-core';
2
+ const debug = createDebugger('gate');
3
+ const SPECIFICITY_COMMAND = 1;
4
+ const SPECIFICITY_SUBCOMMAND = 2;
5
+ const SPECIFICITY_FLAGS = 4;
6
+ const SPECIFICITY_ARGS = 8;
7
+ function calculateSpecificity(rule) {
8
+ let score = SPECIFICITY_COMMAND;
9
+ if (rule.subcommand) {
10
+ score += SPECIFICITY_SUBCOMMAND;
11
+ }
12
+ if (rule.flags) {
13
+ const hasFlags = (rule.flags.required && rule.flags.required.length > 0) ||
14
+ (rule.flags.requiredAll && rule.flags.requiredAll.length > 0) ||
15
+ (rule.flags.forbidden && rule.flags.forbidden.length > 0) ||
16
+ (rule.flags.dangerous && rule.flags.dangerous.length > 0);
17
+ if (hasFlags) {
18
+ score += SPECIFICITY_FLAGS;
19
+ }
20
+ }
21
+ if (rule.args?.pattern) {
22
+ score += SPECIFICITY_ARGS;
23
+ }
24
+ return score;
25
+ }
26
+ function normaliseFlag(flag) {
27
+ if (flag.startsWith('--')) {
28
+ return flag;
29
+ }
30
+ return flag.toLowerCase();
31
+ }
32
+ function hasFlag(parsedFlags, ruleFlag) {
33
+ const normalised = normaliseFlag(ruleFlag);
34
+ return parsedFlags.some((f) => normaliseFlag(f) === normalised);
35
+ }
36
+ function hasAnyFlag(parsedFlags, ruleFlags) {
37
+ return ruleFlags.some((f) => hasFlag(parsedFlags, f));
38
+ }
39
+ function hasAllFlags(parsedFlags, ruleFlags) {
40
+ return ruleFlags.every((f) => hasFlag(parsedFlags, f));
41
+ }
42
+ function matchFlags(parsed, rule) {
43
+ if (!rule.flags) {
44
+ return true;
45
+ }
46
+ const { required, requiredAll, forbidden, dangerous } = rule.flags;
47
+ if (required && required.length > 0) {
48
+ if (!hasAnyFlag(parsed.flags, required)) {
49
+ return false;
50
+ }
51
+ }
52
+ if (requiredAll && requiredAll.length > 0) {
53
+ if (!hasAllFlags(parsed.flags, requiredAll)) {
54
+ return false;
55
+ }
56
+ }
57
+ if (forbidden && forbidden.length > 0) {
58
+ if (hasAnyFlag(parsed.flags, forbidden)) {
59
+ return false;
60
+ }
61
+ }
62
+ if (dangerous && dangerous.length > 0) {
63
+ if (!hasAnyFlag(parsed.flags, dangerous)) {
64
+ return false;
65
+ }
66
+ }
67
+ return true;
68
+ }
69
+ function isPathInTempDir(path, context) {
70
+ const tempPatterns = context?.workingDirectory?.tempDirPatterns ?? ['/tmp', '/var/tmp'];
71
+ return isTempPath(path, tempPatterns);
72
+ }
73
+ function matchArgs(parsed, rule, context) {
74
+ if (!rule.args?.pattern) {
75
+ return true;
76
+ }
77
+ const allArgs = [...parsed.args];
78
+ if (parsed.subcommand) {
79
+ allArgs.unshift(parsed.subcommand);
80
+ }
81
+ if (rule.args.position !== undefined) {
82
+ const arg = allArgs[rule.args.position];
83
+ if (arg === undefined) {
84
+ return false;
85
+ }
86
+ if (context?.workingDirectory?.allowDeleteInCwd && isPathInTempDir(arg, context)) {
87
+ return false;
88
+ }
89
+ return rule.args.pattern.test(arg);
90
+ }
91
+ return allArgs.some((arg) => {
92
+ if (context?.workingDirectory?.allowDeleteInCwd && isPathInTempDir(arg, context)) {
93
+ return false;
94
+ }
95
+ return rule.args.pattern.test(arg);
96
+ });
97
+ }
98
+ function isHomePath(path) {
99
+ return (path.startsWith('/home/') || path.startsWith('/Users/') || path === '~' || path.startsWith('~/'));
100
+ }
101
+ function isRootPath(path) {
102
+ return path === '/' || path === '/root';
103
+ }
104
+ function isTempPath(path, tempPatterns) {
105
+ return tempPatterns.some((pattern) => path.startsWith(pattern));
106
+ }
107
+ function matchWorkingDirectory(ruleCondition, context) {
108
+ if (ruleCondition === 'any') {
109
+ return true;
110
+ }
111
+ const cwd = context?.cwd;
112
+ if (!cwd) {
113
+ return true;
114
+ }
115
+ if (ruleCondition === 'home') {
116
+ return isHomePath(cwd);
117
+ }
118
+ if (ruleCondition === 'root') {
119
+ return isRootPath(cwd);
120
+ }
121
+ return true;
122
+ }
123
+ function matchConditions(rule, context) {
124
+ if (!rule.conditions) {
125
+ return true;
126
+ }
127
+ if (rule.conditions.strictModeOnly && !context?.strict) {
128
+ return false;
129
+ }
130
+ if (rule.conditions.workingDirectory) {
131
+ if (!matchWorkingDirectory(rule.conditions.workingDirectory, context)) {
132
+ return false;
133
+ }
134
+ }
135
+ return true;
136
+ }
137
+ function matchRule(parsed, rule, context) {
138
+ if (!matchConditions(rule, context)) {
139
+ return false;
140
+ }
141
+ if (parsed.command !== rule.command) {
142
+ return false;
143
+ }
144
+ if (rule.subcommand && parsed.subcommand !== rule.subcommand) {
145
+ return false;
146
+ }
147
+ if (!matchFlags(parsed, rule)) {
148
+ return false;
149
+ }
150
+ if (!matchArgs(parsed, rule, context)) {
151
+ return false;
152
+ }
153
+ return true;
154
+ }
155
+ export function findMatchingRule(parsed, rules, context) {
156
+ debug(`findMatchingRule: command=${parsed.command} subcommand=${parsed.subcommand} rules=${rules.length}`);
157
+ const sorted = [...rules].sort((a, b) => {
158
+ const scoreA = calculateSpecificity(a);
159
+ const scoreB = calculateSpecificity(b);
160
+ return scoreB - scoreA;
161
+ });
162
+ for (const rule of sorted) {
163
+ if (matchRule(parsed, rule, context)) {
164
+ debug(`findMatchingRule: matched rule=${rule.id} action=${rule.action}`);
165
+ return rule;
166
+ }
167
+ }
168
+ debug('findMatchingRule: no rule matched');
169
+ return undefined;
170
+ }
171
+ export function analyseCommand(command, parsed, rules, context) {
172
+ debug(`analyseCommand: command=${command}`);
173
+ const matchedRule = findMatchingRule(parsed, rules, context);
174
+ if (!matchedRule) {
175
+ debug('analyseCommand: allowed (no matching rule)');
176
+ return {
177
+ command,
178
+ parsedCommand: parsed,
179
+ action: 'allow',
180
+ severity: 'info',
181
+ };
182
+ }
183
+ debug(`analyseCommand: result action=${matchedRule.action} severity=${matchedRule.severity} rule=${matchedRule.id}`);
184
+ return {
185
+ command,
186
+ parsedCommand: parsed,
187
+ matchedRule,
188
+ action: matchedRule.action,
189
+ severity: matchedRule.severity,
190
+ reason: matchedRule.reason,
191
+ suggestion: matchedRule.suggestion,
192
+ references: matchedRule.references,
193
+ };
194
+ }
195
+ export class RuleMatcher {
196
+ rules;
197
+ context;
198
+ constructor(rules = [], context) {
199
+ this.rules = rules;
200
+ this.context = context;
201
+ }
202
+ setContext(context) {
203
+ this.context = context;
204
+ }
205
+ addRule(rule) {
206
+ this.rules.push(rule);
207
+ }
208
+ addRules(rules) {
209
+ this.rules.push(...rules);
210
+ }
211
+ setRules(rules) {
212
+ this.rules = [...rules];
213
+ }
214
+ getRules() {
215
+ return [...this.rules];
216
+ }
217
+ findMatchingRule(parsed, context) {
218
+ return findMatchingRule(parsed, this.rules, context ?? this.context);
219
+ }
220
+ analyse(command, parsed, context) {
221
+ return analyseCommand(command, parsed, this.rules, context ?? this.context);
222
+ }
223
+ analyseMultiple(commands, context) {
224
+ const ctx = context ?? this.context;
225
+ return commands.map(({ command, parsed }) => analyseCommand(command, parsed, this.rules, ctx));
226
+ }
227
+ }
228
+ export { calculateSpecificity };