@jmylchreest/aide-plugin 0.0.46 → 0.0.47

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jmylchreest/aide-plugin",
3
- "version": "0.0.46",
3
+ "version": "0.0.47",
4
4
  "description": "aide plugin for OpenCode — multi-agent orchestration, memory, skills, and persistence",
5
5
  "type": "module",
6
6
  "main": "./src/opencode/index.ts",
@@ -0,0 +1,70 @@
1
+ ---
2
+ name: semgrep
3
+ description: Run Semgrep security and code quality analysis
4
+ triggers:
5
+ - semgrep
6
+ - security scan
7
+ - sast scan
8
+ - vulnerability scan
9
+ - code security
10
+ - security audit
11
+ requires_binary:
12
+ - semgrep
13
+ ---
14
+
15
+ # Semgrep Security Analysis
16
+
17
+ Run Semgrep to detect security vulnerabilities and code quality issues in the codebase.
18
+
19
+ ## Workflow
20
+
21
+ ### 1. Run Semgrep scan
22
+
23
+ ```bash
24
+ # Auto-detect rules for the project's languages
25
+ semgrep scan --config auto --json --quiet 2>/dev/null | head -c 50000
26
+ ```
27
+
28
+ If JSON output is too large, use text output:
29
+
30
+ ```bash
31
+ semgrep scan --config auto --quiet 2>/dev/null
32
+ ```
33
+
34
+ ### 2. For specific rule sets
35
+
36
+ ```bash
37
+ # Security-focused rules only
38
+ semgrep scan --config "p/security-audit" --json --quiet
39
+
40
+ # OWASP Top 10
41
+ semgrep scan --config "p/owasp-top-ten" --json --quiet
42
+
43
+ # Language-specific
44
+ semgrep scan --config "p/golang" --json --quiet
45
+ semgrep scan --config "p/python" --json --quiet
46
+ semgrep scan --config "p/typescript" --json --quiet
47
+ ```
48
+
49
+ ### 3. Triage results
50
+
51
+ For each finding:
52
+
53
+ 1. Read the file and surrounding context
54
+ 2. Assess whether the finding is a true positive or false positive
55
+ 3. For true positives, fix the issue following the suggestion in the finding
56
+ 4. For false positives, consider adding a `# nosemgrep` inline comment with justification
57
+
58
+ ### 4. Scan specific files
59
+
60
+ ```bash
61
+ # Scan only changed files
62
+ semgrep scan --config auto --json --quiet -- path/to/file.py
63
+ ```
64
+
65
+ ## Common Issues
66
+
67
+ - **Too many findings**: Use `--severity ERROR` to focus on critical issues first
68
+ - **Slow scan**: Use `--config auto` instead of multiple rule packs to avoid re-scanning
69
+ - **Missing rules**: Install additional rules with `semgrep registry`
70
+ - **False positives**: Add `# nosemgrep: rule-id` with a comment explaining why
@@ -8,8 +8,35 @@
8
8
  import { existsSync, readFileSync, readdirSync } from "fs";
9
9
  import { join, basename, extname } from "path";
10
10
  import { homedir } from "os";
11
+ import { execSync } from "child_process";
11
12
  import type { Skill, SkillMatchResult } from "./types.js";
12
13
 
14
+ /**
15
+ * Cache of binary existence checks to avoid repeated shell invocations.
16
+ * Maps binary name to boolean (exists on PATH).
17
+ */
18
+ const binaryExistsCache = new Map<string, boolean>();
19
+
20
+ /**
21
+ * Check if a binary exists on PATH.
22
+ * Results are cached for the lifetime of the process.
23
+ */
24
+ function binaryExists(name: string): boolean {
25
+ const cached = binaryExistsCache.get(name);
26
+ if (cached !== undefined) return cached;
27
+
28
+ try {
29
+ const cmd =
30
+ process.platform === "win32" ? `where ${name}` : `command -v ${name}`;
31
+ execSync(cmd, { stdio: "ignore", timeout: 2000 });
32
+ binaryExistsCache.set(name, true);
33
+ return true;
34
+ } catch {
35
+ binaryExistsCache.set(name, false);
36
+ return false;
37
+ }
38
+ }
39
+
13
40
  // Skill search locations relative to cwd
14
41
  const SKILL_LOCATIONS = [".aide/skills", "skills"];
15
42
 
@@ -129,6 +156,22 @@ export function parseSkillFrontmatter(
129
156
  meta.platforms = platforms;
130
157
  }
131
158
 
159
+ // Parse requires_binary array (e.g. "requires_binary:\n - semgrep")
160
+ const requiresBinary: string[] = [];
161
+ const binaryMatch = yamlContent.match(
162
+ /requires_binary:\s*\n((?:\s+-\s*.+\n?)*)/,
163
+ );
164
+ if (binaryMatch) {
165
+ const blines = binaryMatch[1].split("\n");
166
+ for (const line of blines) {
167
+ const itemMatch = line.match(/^\s+-\s*["']?([^"'\n]+)["']?\s*$/);
168
+ if (itemMatch) requiresBinary.push(itemMatch[1].trim());
169
+ }
170
+ }
171
+ if (requiresBinary.length > 0) {
172
+ meta.requires_binary = requiresBinary;
173
+ }
174
+
132
175
  return { meta, body };
133
176
  }
134
177
 
@@ -176,6 +219,7 @@ export function loadSkill(path: string): Skill | null {
176
219
  triggers,
177
220
  description: meta.description as string | undefined,
178
221
  platforms: meta.platforms as string[] | undefined,
222
+ requires_binary: meta.requires_binary as string[] | undefined,
179
223
  content: body,
180
224
  };
181
225
  } catch {
@@ -253,6 +297,14 @@ export function matchSkills(
253
297
  if (!skill.platforms.includes(platform)) continue;
254
298
  }
255
299
 
300
+ // Binary gate: skip skills that require binaries not on PATH
301
+ if (skill.requires_binary && skill.requires_binary.length > 0) {
302
+ const allPresent = skill.requires_binary.every((bin) =>
303
+ binaryExists(bin),
304
+ );
305
+ if (!allPresent) continue;
306
+ }
307
+
256
308
  let score = 0;
257
309
 
258
310
  for (const trigger of skill.triggers) {
package/src/core/types.ts CHANGED
@@ -113,6 +113,8 @@ export interface Skill {
113
113
  description?: string;
114
114
  /** Optional platform restriction. If set, only matched on listed platforms ("opencode", "claude-code"). */
115
115
  platforms?: string[];
116
+ /** Optional binary requirement. If set, skill is only matched when all listed binaries exist on PATH. */
117
+ requires_binary?: string[];
116
118
  content: string;
117
119
  }
118
120
 
@@ -25,7 +25,7 @@
25
25
  * Stop (blocking) → session.idle re-prompts via session.prompt() for persistence
26
26
  */
27
27
 
28
- import { execFileSync } from "child_process";
28
+ import { execFileSync, execSync } from "child_process";
29
29
  import { join } from "path";
30
30
  import { findAideBinary } from "../core/aide-client.js";
31
31
  import {
@@ -224,6 +224,22 @@ function createConfigHandler(
224
224
  ) {
225
225
  continue;
226
226
  }
227
+ // Skip skills requiring binaries not on PATH
228
+ if (skill.requires_binary && skill.requires_binary.length > 0) {
229
+ const allPresent = skill.requires_binary.every((bin) => {
230
+ try {
231
+ const cmd =
232
+ process.platform === "win32"
233
+ ? `where ${bin}`
234
+ : `command -v ${bin}`;
235
+ execSync(cmd, { stdio: "ignore", timeout: 2000 });
236
+ return true;
237
+ } catch {
238
+ return false;
239
+ }
240
+ });
241
+ if (!allPresent) continue;
242
+ }
227
243
  const commandName = `aide:${skill.name}`;
228
244
  // Only register if not already defined (user config takes priority)
229
245
  if (!input.command[commandName]) {