@probelabs/probe 0.6.0-rc256 → 0.6.0-rc258

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.
@@ -79,9 +79,19 @@ function matchesAnyPattern(parsedCommand, patterns) {
79
79
  export class BashPermissionChecker {
80
80
  /**
81
81
  * Create a permission checker
82
+ *
83
+ * Priority order (highest to lowest):
84
+ * 1. Custom deny — always blocks (user explicitly blocked it)
85
+ * 2. Custom allow — overrides default deny (user explicitly allowed it)
86
+ * 3. Default deny — blocks by default
87
+ * 4. Allow list — allows recognized safe commands
88
+ *
89
+ * This means `--bash-allow "git:push"` overrides the default deny for git:push
90
+ * without requiring `--no-default-bash-deny`.
91
+ *
82
92
  * @param {Object} config - Configuration options
83
- * @param {string[]} [config.allow] - Additional allow patterns
84
- * @param {string[]} [config.deny] - Additional deny patterns
93
+ * @param {string[]} [config.allow] - Additional allow patterns (override default deny)
94
+ * @param {string[]} [config.deny] - Additional deny patterns (always win)
85
95
  * @param {boolean} [config.disableDefaultAllow] - Disable default allow list
86
96
  * @param {boolean} [config.disableDefaultDeny] - Disable default deny list
87
97
  * @param {boolean} [config.debug] - Enable debug logging
@@ -90,38 +100,19 @@ export class BashPermissionChecker {
90
100
  constructor(config = {}) {
91
101
  this.debug = config.debug || false;
92
102
  this.tracer = config.tracer || null;
93
-
94
- // Build allow patterns
95
- this.allowPatterns = [];
96
- if (!config.disableDefaultAllow) {
97
- this.allowPatterns.push(...DEFAULT_ALLOW_PATTERNS);
98
- if (this.debug) {
99
- console.log(`[BashPermissions] Added ${DEFAULT_ALLOW_PATTERNS.length} default allow patterns`);
100
- }
101
- }
102
- if (config.allow && Array.isArray(config.allow)) {
103
- this.allowPatterns.push(...config.allow);
104
- if (this.debug) {
105
- console.log(`[BashPermissions] Added ${config.allow.length} custom allow patterns:`, config.allow);
106
- }
107
- }
108
103
 
109
- // Build deny patterns
110
- this.denyPatterns = [];
111
- if (!config.disableDefaultDeny) {
112
- this.denyPatterns.push(...DEFAULT_DENY_PATTERNS);
113
- if (this.debug) {
114
- console.log(`[BashPermissions] Added ${DEFAULT_DENY_PATTERNS.length} default deny patterns`);
115
- }
116
- }
117
- if (config.deny && Array.isArray(config.deny)) {
118
- this.denyPatterns.push(...config.deny);
119
- if (this.debug) {
120
- console.log(`[BashPermissions] Added ${config.deny.length} custom deny patterns:`, config.deny);
121
- }
122
- }
104
+ // Separate default and custom patterns for priority-based resolution
105
+ this.defaultAllowPatterns = config.disableDefaultAllow ? [] : [...DEFAULT_ALLOW_PATTERNS];
106
+ this.customAllowPatterns = (config.allow && Array.isArray(config.allow)) ? [...config.allow] : [];
107
+ this.allowPatterns = [...this.defaultAllowPatterns, ...this.customAllowPatterns];
108
+
109
+ this.defaultDenyPatterns = config.disableDefaultDeny ? [] : [...DEFAULT_DENY_PATTERNS];
110
+ this.customDenyPatterns = (config.deny && Array.isArray(config.deny)) ? [...config.deny] : [];
111
+ this.denyPatterns = [...this.defaultDenyPatterns, ...this.customDenyPatterns];
123
112
 
124
113
  if (this.debug) {
114
+ console.log(`[BashPermissions] Default allow: ${this.defaultAllowPatterns.length}, Custom allow: ${this.customAllowPatterns.length}`);
115
+ console.log(`[BashPermissions] Default deny: ${this.defaultDenyPatterns.length}, Custom deny: ${this.customDenyPatterns.length}`);
125
116
  console.log(`[BashPermissions] Total patterns - Allow: ${this.allowPatterns.length}, Deny: ${this.denyPatterns.length}`);
126
117
  }
127
118
 
@@ -129,8 +120,8 @@ export class BashPermissionChecker {
129
120
  this.recordBashEvent('permissions.initialized', {
130
121
  allowPatternCount: this.allowPatterns.length,
131
122
  denyPatternCount: this.denyPatterns.length,
132
- hasCustomAllowPatterns: !!(config.allow && config.allow.length > 0),
133
- hasCustomDenyPatterns: !!(config.deny && config.deny.length > 0),
123
+ hasCustomAllowPatterns: this.customAllowPatterns.length > 0,
124
+ hasCustomDenyPatterns: this.customDenyPatterns.length > 0,
134
125
  disableDefaultAllow: !!config.disableDefaultAllow,
135
126
  disableDefaultDeny: !!config.disableDefaultDeny
136
127
  });
@@ -212,9 +203,18 @@ export class BashPermissionChecker {
212
203
  console.log(`[BashPermissions] Parsed: ${parsed.command} with args: [${parsed.args.join(', ')}]`);
213
204
  }
214
205
 
215
- // Check deny patterns first (deny takes precedence)
216
- if (matchesAnyPattern(parsed, this.denyPatterns)) {
217
- const matchedPatterns = this.denyPatterns.filter(pattern => matchesPattern(parsed, pattern));
206
+ // Priority-based permission check:
207
+ // 1. Custom deny always wins
208
+ // 2. Custom allow overrides default deny
209
+ // 3. Default deny blocks
210
+ // 4. Allow list permits
211
+
212
+ // Step 1: Custom deny always wins
213
+ if (matchesAnyPattern(parsed, this.customDenyPatterns)) {
214
+ const matchedPatterns = this.customDenyPatterns.filter(pattern => matchesPattern(parsed, pattern));
215
+ if (this.debug) {
216
+ console.log(`[BashPermissions] DENIED - matches custom deny pattern: ${matchedPatterns[0]}`);
217
+ }
218
218
  const result = {
219
219
  allowed: false,
220
220
  reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
@@ -227,12 +227,40 @@ export class BashPermissionChecker {
227
227
  parsedCommand: parsed.command,
228
228
  reason: 'matches_deny_pattern',
229
229
  matchedPattern: matchedPatterns[0],
230
- isComplex: false
230
+ isComplex: false,
231
+ isCustomDeny: true
231
232
  });
232
233
  return result;
233
234
  }
234
235
 
235
- // Check allow patterns
236
+ // Step 2: Custom allow overrides default deny
237
+ const matchesCustomAllow = matchesAnyPattern(parsed, this.customAllowPatterns);
238
+
239
+ // Step 3: Default deny (skipped if custom allow matches)
240
+ if (!matchesCustomAllow && matchesAnyPattern(parsed, this.defaultDenyPatterns)) {
241
+ const matchedPatterns = this.defaultDenyPatterns.filter(pattern => matchesPattern(parsed, pattern));
242
+ if (this.debug) {
243
+ console.log(`[BashPermissions] DENIED - matches default deny pattern: ${matchedPatterns[0]}`);
244
+ }
245
+ const result = {
246
+ allowed: false,
247
+ reason: `Command matches deny pattern: ${matchedPatterns[0]}`,
248
+ command: command,
249
+ parsed: parsed,
250
+ matchedPatterns: matchedPatterns
251
+ };
252
+ this.recordBashEvent('permission.denied', {
253
+ command,
254
+ parsedCommand: parsed.command,
255
+ reason: 'matches_deny_pattern',
256
+ matchedPattern: matchedPatterns[0],
257
+ isComplex: false,
258
+ isCustomDeny: false
259
+ });
260
+ return result;
261
+ }
262
+
263
+ // Step 4: Check allow patterns
236
264
  if (this.allowPatterns.length > 0) {
237
265
  if (!matchesAnyPattern(parsed, this.allowPatterns)) {
238
266
  const result = {
@@ -256,17 +284,23 @@ export class BashPermissionChecker {
256
284
  allowed: true,
257
285
  command: command,
258
286
  parsed: parsed,
259
- isComplex: false
287
+ isComplex: false,
288
+ overriddenDeny: matchesCustomAllow && matchesAnyPattern(parsed, this.defaultDenyPatterns)
260
289
  };
261
290
 
262
291
  if (this.debug) {
263
- console.log(`[BashPermissions] ALLOWED - command passed all checks`);
292
+ if (result.overriddenDeny) {
293
+ console.log(`[BashPermissions] ALLOWED - custom allow overrides default deny`);
294
+ } else {
295
+ console.log(`[BashPermissions] ALLOWED - command passed all checks`);
296
+ }
264
297
  }
265
298
 
266
299
  this.recordBashEvent('permission.allowed', {
267
300
  command,
268
301
  parsedCommand: parsed.command,
269
- isComplex: false
302
+ isComplex: false,
303
+ overriddenDeny: result.overriddenDeny || false
270
304
  });
271
305
 
272
306
  return result;
@@ -477,10 +511,25 @@ export class BashPermissionChecker {
477
511
  break;
478
512
  }
479
513
 
480
- // Check against deny patterns
481
- if (matchesAnyPattern(parsed, this.denyPatterns)) {
514
+ // Check using same priority logic as simple commands:
515
+ // 1. Custom deny always wins
516
+ if (matchesAnyPattern(parsed, this.customDenyPatterns)) {
517
+ if (this.debug) {
518
+ console.log(`[BashPermissions] Component "${component}" matches custom deny pattern`);
519
+ }
520
+ allAllowed = false;
521
+ deniedComponent = component;
522
+ deniedReason = 'Component matches deny pattern';
523
+ break;
524
+ }
525
+
526
+ // 2. Custom allow overrides default deny
527
+ const componentMatchesCustomAllow = matchesAnyPattern(parsed, this.customAllowPatterns);
528
+
529
+ // 3. Default deny (skipped if custom allow matches)
530
+ if (!componentMatchesCustomAllow && matchesAnyPattern(parsed, this.defaultDenyPatterns)) {
482
531
  if (this.debug) {
483
- console.log(`[BashPermissions] Component "${component}" matches deny pattern`);
532
+ console.log(`[BashPermissions] Component "${component}" matches default deny pattern`);
484
533
  }
485
534
  allAllowed = false;
486
535
  deniedComponent = component;
@@ -488,7 +537,7 @@ export class BashPermissionChecker {
488
537
  break;
489
538
  }
490
539
 
491
- // Check against allow patterns
540
+ // 4. Check allow patterns
492
541
  if (!matchesAnyPattern(parsed, this.allowPatterns)) {
493
542
  if (this.debug) {
494
543
  console.log(`[BashPermissions] Component "${component}" not in allow list`);
@@ -567,6 +616,10 @@ export class BashPermissionChecker {
567
616
  return {
568
617
  allowPatterns: this.allowPatterns.length,
569
618
  denyPatterns: this.denyPatterns.length,
619
+ customAllowPatterns: this.customAllowPatterns.length,
620
+ customDenyPatterns: this.customDenyPatterns.length,
621
+ defaultAllowPatterns: this.defaultAllowPatterns.length,
622
+ defaultDenyPatterns: this.defaultDenyPatterns.length,
570
623
  totalPatterns: this.allowPatterns.length + this.denyPatterns.length
571
624
  };
572
625
  }
@@ -56,20 +56,52 @@ When reviewing code:
56
56
 
57
57
  'code-review-template': `You are going to perform code review according to provided user rules. Ensure to review only code provided in diff and latest commit, if provided. However you still need to fully understand how modified code works, and read dependencies if something is not clear.`,
58
58
 
59
- 'engineer': `You are senior engineer focused on software architecture and design.
60
- Before jumping on the task you first, in details analyse user request, and try to provide elegant and concise solution.
61
- If solution is clear, you can jump to implementation right away, if not, you can ask user a clarification question, by calling attempt_completion tool, with required details.
59
+ 'engineer': `You are a senior engineer focused on software architecture and design.
60
+ Before jumping on the task you first analyse the user request in detail, and try to provide an elegant and concise solution.
61
+ If the solution is clear, you can jump to implementation right away. If not, ask the user a clarification question by calling the attempt_completion tool with the required details.
62
62
 
63
- Before jumping to implementation:
63
+ # Tone and Style
64
+ - Be concise and direct. Explain your approach briefly before implementing, then let the code speak for itself.
65
+ - Do not add unnecessary preamble or postamble. Skip "Here is what I will do" or "Here is a summary of changes" unless the user asks.
66
+ - Do not add code comments unless the logic is genuinely complex and non-obvious.
67
+
68
+ # Before Implementation
64
69
  - Focus on high-level design patterns and system organization
65
70
  - Identify architectural patterns and component relationships
66
71
  - Evaluate system structure and suggest architectural improvements
67
- - Focus on backward compatibility.
72
+ - Focus on backward compatibility
68
73
  - Consider scalability, maintainability, and extensibility in your analysis
69
74
 
70
- During the implementation:
71
- - Avoid implementing special cases
72
- - Do not forget to add the tests`,
75
+ # Following Conventions
76
+ - NEVER assume a library or dependency is available. Before using any library, check the project's dependency file (package.json, Cargo.toml, go.mod, requirements.txt, etc.) to confirm it exists in the project.
77
+ - Before writing new code, look at neighboring files and existing implementations to understand the project's code style, naming conventions, and patterns. Mimic them.
78
+ - Check imports and existing utilities before creating new helpers — the project may already have what you need.
79
+
80
+ # Task Planning
81
+ - If the task tool is available, use it to break complex work into milestones before starting implementation.
82
+ - Stay flexible — if your understanding changes mid-task, add, remove, or reorganize tasks as needed. The plan should serve you, not constrain you.
83
+
84
+ # During Implementation
85
+ - Always create a new branch before making changes to the codebase.
86
+ - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
87
+ - Avoid implementing special cases when a general approach works
88
+ - Never expose secrets, API keys, or credentials in generated code. Never log sensitive information.
89
+ - Do not surprise the user with unrequested changes. Do what was asked, including reasonable follow-up actions, but do not refactor surrounding code or add features that were not requested.
90
+ - After every significant change, verify the project still builds and passes linting. Do not wait until the end to discover breakage.
91
+
92
+ # After Implementation
93
+ - Always run the project's tests before considering the task complete. If tests fail, fix them.
94
+ - Run lint and typecheck commands if known for the project.
95
+ - If a build, lint, or test fails, fix the issue before finishing.
96
+ - When the task is done, respond to the user with a concise summary of what was implemented, what files were changed, and any relevant details. Include links (e.g. pull request URL) so the user has everything they need.
97
+
98
+ # GitHub Integration
99
+ - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
100
+ - To create a pull request: commit your changes, push the branch, then use \`gh pr create --title "..." --body "..."\`.
101
+ - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
102
+ - If given a GitHub URL, use \`gh\` to fetch the relevant information rather than guessing.
103
+ - Always return the pull request URL to the user after creating one.
104
+ - When checking GitHub Actions, only read logs of failed jobs — do not waste time on successful ones. Use \`gh run view <run-id> --log-failed\` to fetch only the relevant output.`,
73
105
 
74
106
  'support': `You are ProbeChat Support, a specialized AI assistant focused on helping developers troubleshoot issues and solve problems. Your primary function is to help users diagnose errors, understand unexpected behaviors, and find solutions using the provided code analysis tools.
75
107
 
package/src/tools/bash.js CHANGED
@@ -146,8 +146,8 @@ Common reasons:
146
146
  2. The command is not in the allow list (not a recognized safe command)
147
147
 
148
148
  If you believe this command should be allowed, you can:
149
- - Use the --bash-allow option to add specific patterns
150
- - Use the --no-default-bash-deny flag to remove default restrictions (not recommended)
149
+ - Use the --bash-allow option to add specific patterns (overrides default deny list)
150
+ Example: --bash-allow "git:push" allows git push while keeping all other deny rules
151
151
 
152
152
  For code exploration, try these safe alternatives:
153
153
  - ls, cat, head, tail for file operations