agentsys 5.0.3 → 5.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.
- package/.claude-plugin/marketplace.json +21 -14
- package/.claude-plugin/plugin.json +1 -1
- package/AGENTS.md +2 -1
- package/CHANGELOG.md +18 -0
- package/README.md +7 -6
- package/adapters/codex/skills/agnix/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-agents/SKILL.md +0 -1
- package/adapters/codex/skills/audit-project-github/SKILL.md +0 -1
- package/adapters/codex/skills/consult/SKILL.md +132 -57
- package/adapters/codex/skills/debate/SKILL.md +214 -0
- package/adapters/codex/skills/delivery-approval/SKILL.md +0 -1
- package/adapters/codex/skills/deslop/SKILL.md +0 -1
- package/adapters/codex/skills/drift-detect/SKILL.md +0 -1
- package/adapters/codex/skills/enhance/SKILL.md +0 -1
- package/adapters/codex/skills/learn/SKILL.md +0 -1
- package/adapters/codex/skills/next-task/SKILL.md +0 -1
- package/adapters/codex/skills/perf/SKILL.md +0 -1
- package/adapters/codex/skills/repo-map/SKILL.md +0 -1
- package/adapters/codex/skills/ship/SKILL.md +0 -1
- package/adapters/codex/skills/ship-ci-review-loop/SKILL.md +0 -1
- package/adapters/codex/skills/ship-deployment/SKILL.md +0 -1
- package/adapters/codex/skills/ship-error-handling/SKILL.md +0 -1
- package/adapters/codex/skills/sync-docs/SKILL.md +0 -1
- package/adapters/opencode/agents/agent-enhancer.md +0 -1
- package/adapters/opencode/agents/agnix-agent.md +0 -1
- package/adapters/opencode/agents/ci-fixer.md +0 -1
- package/adapters/opencode/agents/ci-monitor.md +0 -1
- package/adapters/opencode/agents/claudemd-enhancer.md +0 -1
- package/adapters/opencode/agents/consult-agent.md +122 -30
- package/adapters/opencode/agents/cross-file-enhancer.md +0 -1
- package/adapters/opencode/agents/debate-orchestrator.md +169 -0
- package/adapters/opencode/agents/delivery-validator.md +0 -1
- package/adapters/opencode/agents/deslop-agent.md +0 -1
- package/adapters/opencode/agents/docs-enhancer.md +0 -1
- package/adapters/opencode/agents/exploration-agent.md +0 -1
- package/adapters/opencode/agents/hooks-enhancer.md +0 -1
- package/adapters/opencode/agents/implementation-agent.md +0 -1
- package/adapters/opencode/agents/learn-agent.md +0 -1
- package/adapters/opencode/agents/map-validator.md +0 -1
- package/adapters/opencode/agents/perf-analyzer.md +0 -1
- package/adapters/opencode/agents/perf-code-paths.md +0 -1
- package/adapters/opencode/agents/perf-investigation-logger.md +0 -1
- package/adapters/opencode/agents/perf-orchestrator.md +0 -1
- package/adapters/opencode/agents/perf-theory-gatherer.md +0 -1
- package/adapters/opencode/agents/perf-theory-tester.md +0 -1
- package/adapters/opencode/agents/plan-synthesizer.md +0 -1
- package/adapters/opencode/agents/planning-agent.md +0 -1
- package/adapters/opencode/agents/plugin-enhancer.md +0 -1
- package/adapters/opencode/agents/prompt-enhancer.md +0 -1
- package/adapters/opencode/agents/simple-fixer.md +0 -1
- package/adapters/opencode/agents/skills-enhancer.md +0 -1
- package/adapters/opencode/agents/sync-docs-agent.md +0 -1
- package/adapters/opencode/agents/task-discoverer.md +0 -1
- package/adapters/opencode/agents/test-coverage-checker.md +0 -1
- package/adapters/opencode/agents/worktree-manager.md +0 -1
- package/adapters/opencode/commands/agnix.md +0 -1
- package/adapters/opencode/commands/audit-project-agents.md +0 -1
- package/adapters/opencode/commands/audit-project-github.md +0 -1
- package/adapters/opencode/commands/audit-project.md +0 -1
- package/adapters/opencode/commands/consult.md +133 -57
- package/adapters/opencode/commands/debate.md +224 -0
- package/adapters/opencode/commands/delivery-approval.md +0 -1
- package/adapters/opencode/commands/deslop.md +0 -1
- package/adapters/opencode/commands/drift-detect.md +0 -1
- package/adapters/opencode/commands/enhance.md +0 -1
- package/adapters/opencode/commands/learn.md +0 -1
- package/adapters/opencode/commands/next-task.md +0 -1
- package/adapters/opencode/commands/perf.md +0 -1
- package/adapters/opencode/commands/repo-map.md +0 -1
- package/adapters/opencode/commands/ship-ci-review-loop.md +0 -1
- package/adapters/opencode/commands/ship-deployment.md +0 -1
- package/adapters/opencode/commands/ship-error-handling.md +0 -1
- package/adapters/opencode/commands/ship.md +0 -1
- package/adapters/opencode/commands/sync-docs.md +0 -1
- package/adapters/opencode/skills/agnix/SKILL.md +1 -2
- package/adapters/opencode/skills/consult/SKILL.md +33 -23
- package/adapters/opencode/skills/debate/SKILL.md +245 -0
- package/adapters/opencode/skills/deslop/SKILL.md +1 -2
- package/adapters/opencode/skills/discover-tasks/SKILL.md +1 -2
- package/adapters/opencode/skills/drift-analysis/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-agent-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-claude-memory/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-cross-file/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-hooks/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-orchestrator/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-plugins/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-prompts/SKILL.md +1 -2
- package/adapters/opencode/skills/enhance-skills/SKILL.md +1 -2
- package/adapters/opencode/skills/learn/SKILL.md +1 -2
- package/adapters/opencode/skills/orchestrate-review/SKILL.md +0 -1
- package/adapters/opencode/skills/perf-analyzer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-baseline-manager/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-benchmarker/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-code-paths/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-investigation-logger/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-profiler/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-gatherer/SKILL.md +1 -2
- package/adapters/opencode/skills/perf-theory-tester/SKILL.md +1 -2
- package/adapters/opencode/skills/repo-mapping/SKILL.md +1 -2
- package/adapters/opencode/skills/sync-docs/SKILL.md +1 -2
- package/adapters/opencode/skills/validate-delivery/SKILL.md +1 -2
- package/lib/adapter-transforms.js +24 -4
- package/package.json +1 -1
- package/plugins/agnix/.claude-plugin/plugin.json +1 -1
- package/plugins/agnix/skills/agnix/SKILL.md +1 -1
- package/plugins/audit-project/.claude-plugin/plugin.json +1 -1
- package/plugins/audit-project/lib/adapter-transforms.js +24 -4
- package/plugins/consult/.claude-plugin/plugin.json +1 -1
- package/plugins/consult/agents/consult-agent.md +122 -29
- package/plugins/consult/commands/consult.md +135 -58
- package/plugins/consult/skills/consult/SKILL.md +31 -20
- package/plugins/debate/.claude-plugin/plugin.json +21 -0
- package/plugins/debate/agents/debate-orchestrator.md +175 -0
- package/plugins/debate/commands/debate.md +221 -0
- package/plugins/debate/lib/adapter-transforms.js +298 -0
- package/plugins/debate/lib/collectors/codebase.js +392 -0
- package/plugins/debate/lib/collectors/docs-patterns.js +713 -0
- package/plugins/debate/lib/collectors/documentation.js +219 -0
- package/plugins/debate/lib/collectors/github.js +330 -0
- package/plugins/debate/lib/collectors/index.js +126 -0
- package/plugins/debate/lib/config/index.js +14 -0
- package/plugins/debate/lib/cross-platform/index.js +539 -0
- package/plugins/debate/lib/discovery/index.js +352 -0
- package/plugins/debate/lib/drift-detect/collectors.js +37 -0
- package/plugins/debate/lib/enhance/agent-analyzer.js +421 -0
- package/plugins/debate/lib/enhance/agent-patterns.js +571 -0
- package/plugins/debate/lib/enhance/auto-suppression.js +622 -0
- package/plugins/debate/lib/enhance/benchmark.js +417 -0
- package/plugins/debate/lib/enhance/cross-file-analyzer.js +930 -0
- package/plugins/debate/lib/enhance/cross-file-patterns.js +370 -0
- package/plugins/debate/lib/enhance/docs-analyzer.js +325 -0
- package/plugins/debate/lib/enhance/docs-patterns.js +671 -0
- package/plugins/debate/lib/enhance/fixer.js +721 -0
- package/plugins/debate/lib/enhance/hook-analyzer.js +135 -0
- package/plugins/debate/lib/enhance/hook-patterns.js +40 -0
- package/plugins/debate/lib/enhance/index.js +127 -0
- package/plugins/debate/lib/enhance/plugin-analyzer.js +402 -0
- package/plugins/debate/lib/enhance/plugin-patterns.js +326 -0
- package/plugins/debate/lib/enhance/projectmemory-analyzer.js +551 -0
- package/plugins/debate/lib/enhance/projectmemory-patterns.js +617 -0
- package/plugins/debate/lib/enhance/prompt-analyzer.js +457 -0
- package/plugins/debate/lib/enhance/prompt-patterns.js +1484 -0
- package/plugins/debate/lib/enhance/reporter.js +1348 -0
- package/plugins/debate/lib/enhance/security-patterns.js +284 -0
- package/plugins/debate/lib/enhance/skill-analyzer.js +182 -0
- package/plugins/debate/lib/enhance/skill-patterns.js +147 -0
- package/plugins/debate/lib/enhance/suppression.js +352 -0
- package/plugins/debate/lib/enhance/tool-patterns.js +373 -0
- package/plugins/debate/lib/index.js +270 -0
- package/plugins/debate/lib/patterns/cli-enhancers.js +611 -0
- package/plugins/debate/lib/patterns/pipeline.js +948 -0
- package/plugins/debate/lib/patterns/review-patterns.js +558 -0
- package/plugins/debate/lib/patterns/slop-analyzers.js +2305 -0
- package/plugins/debate/lib/patterns/slop-patterns.js +1187 -0
- package/plugins/debate/lib/perf/analyzer/index.js +22 -0
- package/plugins/debate/lib/perf/argument-parser.js +105 -0
- package/plugins/debate/lib/perf/baseline-comparator.js +50 -0
- package/plugins/debate/lib/perf/baseline-store.js +127 -0
- package/plugins/debate/lib/perf/benchmark-runner.js +404 -0
- package/plugins/debate/lib/perf/breaking-point-finder.js +52 -0
- package/plugins/debate/lib/perf/breaking-point-runner.js +60 -0
- package/plugins/debate/lib/perf/checkpoint.js +123 -0
- package/plugins/debate/lib/perf/code-paths.js +86 -0
- package/plugins/debate/lib/perf/consolidation.js +37 -0
- package/plugins/debate/lib/perf/constraint-runner.js +71 -0
- package/plugins/debate/lib/perf/experiment-runner.js +32 -0
- package/plugins/debate/lib/perf/index.js +41 -0
- package/plugins/debate/lib/perf/investigation-state.js +874 -0
- package/plugins/debate/lib/perf/optimization-runner.js +79 -0
- package/plugins/debate/lib/perf/profilers/go.js +22 -0
- package/plugins/debate/lib/perf/profilers/index.js +46 -0
- package/plugins/debate/lib/perf/profilers/java.js +23 -0
- package/plugins/debate/lib/perf/profilers/node.js +27 -0
- package/plugins/debate/lib/perf/profilers/python.js +23 -0
- package/plugins/debate/lib/perf/profilers/rust.js +23 -0
- package/plugins/debate/lib/perf/profiling-runner.js +75 -0
- package/plugins/debate/lib/perf/schemas.js +140 -0
- package/plugins/debate/lib/platform/detect-platform.js +413 -0
- package/plugins/debate/lib/platform/detection-configs.js +93 -0
- package/plugins/debate/lib/platform/state-dir.js +132 -0
- package/plugins/debate/lib/platform/verify-tools.js +182 -0
- package/plugins/debate/lib/repo-map/cache.js +152 -0
- package/plugins/debate/lib/repo-map/concurrency.js +29 -0
- package/plugins/debate/lib/repo-map/index.js +222 -0
- package/plugins/debate/lib/repo-map/installer.js +212 -0
- package/plugins/debate/lib/repo-map/queries/go.js +27 -0
- package/plugins/debate/lib/repo-map/queries/index.js +100 -0
- package/plugins/debate/lib/repo-map/queries/java.js +38 -0
- package/plugins/debate/lib/repo-map/queries/javascript.js +55 -0
- package/plugins/debate/lib/repo-map/queries/python.js +24 -0
- package/plugins/debate/lib/repo-map/queries/rust.js +73 -0
- package/plugins/debate/lib/repo-map/queries/typescript.js +38 -0
- package/plugins/debate/lib/repo-map/runner.js +1364 -0
- package/plugins/debate/lib/repo-map/updater.js +562 -0
- package/plugins/debate/lib/repo-map/usage-analyzer.js +407 -0
- package/plugins/debate/lib/schemas/plugin-manifest.schema.json +57 -0
- package/plugins/debate/lib/schemas/validator.js +247 -0
- package/plugins/debate/lib/sources/custom-handler.js +199 -0
- package/plugins/debate/lib/sources/policy-questions.js +246 -0
- package/plugins/debate/lib/sources/source-cache.js +165 -0
- package/plugins/debate/lib/state/workflow-state.js +576 -0
- package/plugins/debate/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/debate/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/debate/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/debate/lib/types/index.d.ts +84 -0
- package/plugins/debate/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/debate/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/debate/lib/utils/atomic-write.js +94 -0
- package/plugins/debate/lib/utils/cache-manager.js +159 -0
- package/plugins/debate/lib/utils/command-parser.js +0 -0
- package/plugins/debate/lib/utils/context-optimizer.js +300 -0
- package/plugins/debate/lib/utils/deprecation.js +37 -0
- package/plugins/debate/lib/utils/shell-escape.js +88 -0
- package/plugins/debate/lib/utils/state-helpers.js +61 -0
- package/plugins/debate/skills/debate/SKILL.md +264 -0
- package/plugins/deslop/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop/lib/adapter-transforms.js +24 -4
- package/plugins/deslop/skills/deslop/SKILL.md +1 -1
- package/plugins/drift-detect/.claude-plugin/plugin.json +1 -1
- package/plugins/drift-detect/lib/adapter-transforms.js +24 -4
- package/plugins/drift-detect/skills/drift-analysis/SKILL.md +1 -1
- package/plugins/enhance/.claude-plugin/plugin.json +1 -1
- package/plugins/enhance/lib/adapter-transforms.js +24 -4
- package/plugins/enhance/skills/enhance-agent-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-claude-memory/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-cross-file/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-docs/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-hooks/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-orchestrator/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-plugins/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-prompts/SKILL.md +1 -1
- package/plugins/enhance/skills/enhance-skills/SKILL.md +1 -1
- package/plugins/learn/.claude-plugin/plugin.json +1 -1
- package/plugins/learn/agents/learn-agent.md +1 -1
- package/plugins/learn/lib/adapter-transforms.js +24 -4
- package/plugins/learn/skills/learn/SKILL.md +1 -1
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/exploration-agent.md +1 -1
- package/plugins/next-task/lib/adapter-transforms.js +24 -4
- package/plugins/next-task/skills/discover-tasks/SKILL.md +1 -1
- package/plugins/next-task/skills/validate-delivery/SKILL.md +1 -1
- package/plugins/perf/.claude-plugin/plugin.json +1 -1
- package/plugins/perf/lib/adapter-transforms.js +24 -4
- package/plugins/perf/skills/perf-analyzer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-baseline-manager/SKILL.md +1 -1
- package/plugins/perf/skills/perf-benchmarker/SKILL.md +1 -1
- package/plugins/perf/skills/perf-code-paths/SKILL.md +1 -1
- package/plugins/perf/skills/perf-investigation-logger/SKILL.md +1 -1
- package/plugins/perf/skills/perf-profiler/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-gatherer/SKILL.md +1 -1
- package/plugins/perf/skills/perf-theory-tester/SKILL.md +1 -1
- package/plugins/repo-map/.claude-plugin/plugin.json +1 -1
- package/plugins/repo-map/lib/adapter-transforms.js +24 -4
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/.claude-plugin/plugin.json +1 -1
- package/plugins/sync-docs/lib/adapter-transforms.js +24 -4
- package/plugins/sync-docs/skills/sync-docs/SKILL.md +1 -1
- package/scripts/gen-adapters.js +6 -7
- package/scripts/generate-docs.js +4 -2
- package/scripts/plugins.txt +1 -0
- package/site/content.json +6 -6
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Patterns
|
|
3
|
+
* Detection patterns for security vulnerabilities in plugins
|
|
4
|
+
*
|
|
5
|
+
* @author Avi Fenesh
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Security patterns with certainty levels
|
|
11
|
+
*/
|
|
12
|
+
const securityPatterns = {
|
|
13
|
+
/**
|
|
14
|
+
* Unrestricted Bash tool access
|
|
15
|
+
* HIGH certainty - security risk
|
|
16
|
+
*/
|
|
17
|
+
unrestricted_bash: {
|
|
18
|
+
id: 'unrestricted_bash',
|
|
19
|
+
category: 'security',
|
|
20
|
+
certainty: 'HIGH',
|
|
21
|
+
autoFix: false,
|
|
22
|
+
description: 'Agent has unrestricted Bash tool access',
|
|
23
|
+
pattern: /^tools:\s*.*\bBash\b(?!\s*\()/m,
|
|
24
|
+
check: (content, filePath) => {
|
|
25
|
+
// Check for Bash without restrictions in agent frontmatter
|
|
26
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
27
|
+
if (!frontmatterMatch) return null;
|
|
28
|
+
|
|
29
|
+
const frontmatter = frontmatterMatch[1];
|
|
30
|
+
const toolsMatch = frontmatter.match(/^tools:[ \t]*(\S.*)?$/m);
|
|
31
|
+
if (!toolsMatch) return null;
|
|
32
|
+
|
|
33
|
+
const tools = toolsMatch[1];
|
|
34
|
+
// Bash without parentheses means unrestricted
|
|
35
|
+
if (/\bBash\b(?!\s*\()/.test(tools)) {
|
|
36
|
+
return {
|
|
37
|
+
issue: 'Unrestricted Bash access',
|
|
38
|
+
fix: 'Add restrictions like Bash(git:*) or Bash(npm:*)',
|
|
39
|
+
line: content.substring(0, frontmatterMatch.index + frontmatterMatch[0].indexOf(toolsMatch[0])).split('\n').length
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Command injection via string interpolation
|
|
48
|
+
* HIGH certainty - dangerous pattern
|
|
49
|
+
*/
|
|
50
|
+
command_injection: {
|
|
51
|
+
id: 'command_injection',
|
|
52
|
+
category: 'security',
|
|
53
|
+
certainty: 'HIGH',
|
|
54
|
+
autoFix: false,
|
|
55
|
+
description: 'Potential command injection via string interpolation',
|
|
56
|
+
pattern: /\$\{[^}]*\}/,
|
|
57
|
+
check: (content, filePath) => {
|
|
58
|
+
const issues = [];
|
|
59
|
+
const lines = content.split('\n');
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < lines.length; i++) {
|
|
62
|
+
const line = lines[i];
|
|
63
|
+
// Look for shell commands with interpolation (string checks avoid ReDoS)
|
|
64
|
+
if (/\b(?:exec|spawn|system|shell)\b|`|Bash/i.test(line) && /[(`]/.test(line) && line.includes('${')) {
|
|
65
|
+
issues.push({
|
|
66
|
+
issue: 'Command injection risk via string interpolation',
|
|
67
|
+
fix: 'Validate and escape user input before shell execution',
|
|
68
|
+
line: i + 1
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return issues.length > 0 ? issues : null;
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Path traversal patterns
|
|
79
|
+
* HIGH certainty - security risk
|
|
80
|
+
*/
|
|
81
|
+
path_traversal: {
|
|
82
|
+
id: 'path_traversal',
|
|
83
|
+
category: 'security',
|
|
84
|
+
certainty: 'HIGH',
|
|
85
|
+
autoFix: false,
|
|
86
|
+
description: 'Potential path traversal vulnerability',
|
|
87
|
+
pattern: /\.\.\//,
|
|
88
|
+
check: (content, filePath) => {
|
|
89
|
+
const issues = [];
|
|
90
|
+
const lines = content.split('\n');
|
|
91
|
+
|
|
92
|
+
for (let i = 0; i < lines.length; i++) {
|
|
93
|
+
const line = lines[i];
|
|
94
|
+
// Look for user-controlled paths with ../ (string checks avoid ReDoS)
|
|
95
|
+
if (/\b(?:path|file|dir)\b/i.test(line) && line.includes('$') && line.includes('../')) {
|
|
96
|
+
issues.push({
|
|
97
|
+
issue: 'Path traversal risk - user input may contain ../',
|
|
98
|
+
fix: 'Validate paths and use path.resolve() with base directory check',
|
|
99
|
+
line: i + 1
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return issues.length > 0 ? issues : null;
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Hardcoded secrets in agent files
|
|
110
|
+
* HIGH certainty - critical
|
|
111
|
+
*/
|
|
112
|
+
hardcoded_secrets: {
|
|
113
|
+
id: 'hardcoded_secrets',
|
|
114
|
+
category: 'security',
|
|
115
|
+
certainty: 'HIGH',
|
|
116
|
+
autoFix: false,
|
|
117
|
+
description: 'Potential hardcoded secrets',
|
|
118
|
+
pattern: /(api[_-]?key|secret|token|password|credential)\s*[:=]\s*["'`][^"'`\s]{8,}["'`]/i,
|
|
119
|
+
check: (content, filePath) => {
|
|
120
|
+
const issues = [];
|
|
121
|
+
const lines = content.split('\n');
|
|
122
|
+
const secretPattern = /(api[_-]?key|secret|token|password|credential)\s*[:=]\s*["'`](?!\$\{)(?!\{\{)[^"'`\s]{8,}["'`]/i;
|
|
123
|
+
|
|
124
|
+
for (let i = 0; i < lines.length; i++) {
|
|
125
|
+
const line = lines[i];
|
|
126
|
+
if (secretPattern.test(line)) {
|
|
127
|
+
issues.push({
|
|
128
|
+
issue: 'Potential hardcoded secret',
|
|
129
|
+
fix: 'Use environment variables instead',
|
|
130
|
+
line: i + 1
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return issues.length > 0 ? issues : null;
|
|
136
|
+
}
|
|
137
|
+
},
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Missing input validation
|
|
141
|
+
* MEDIUM certainty - may be intentional
|
|
142
|
+
*/
|
|
143
|
+
missing_input_validation: {
|
|
144
|
+
id: 'missing_input_validation',
|
|
145
|
+
category: 'security',
|
|
146
|
+
certainty: 'MEDIUM',
|
|
147
|
+
autoFix: false,
|
|
148
|
+
description: 'User input used without validation',
|
|
149
|
+
check: (content, filePath) => {
|
|
150
|
+
const issues = [];
|
|
151
|
+
const lines = content.split('\n');
|
|
152
|
+
|
|
153
|
+
for (let i = 0; i < lines.length; i++) {
|
|
154
|
+
const line = lines[i];
|
|
155
|
+
// Look for direct use of ARGUMENTS without validation
|
|
156
|
+
if (/\$ARGUMENTS/.test(line) && !/validate|check|verify|sanitize/.test(lines.slice(Math.max(0, i - 5), i + 1).join('\n').toLowerCase())) {
|
|
157
|
+
issues.push({
|
|
158
|
+
issue: 'User input ($ARGUMENTS) used without apparent validation',
|
|
159
|
+
fix: 'Add input validation before using user-provided arguments',
|
|
160
|
+
line: i + 1
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return issues.length > 0 ? issues : null;
|
|
166
|
+
}
|
|
167
|
+
},
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Broad file access patterns
|
|
171
|
+
* MEDIUM certainty - may be required
|
|
172
|
+
*/
|
|
173
|
+
broad_file_access: {
|
|
174
|
+
id: 'broad_file_access',
|
|
175
|
+
category: 'security',
|
|
176
|
+
certainty: 'MEDIUM',
|
|
177
|
+
autoFix: false,
|
|
178
|
+
description: 'Agent requests broad file system access',
|
|
179
|
+
check: (content, filePath) => {
|
|
180
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
181
|
+
if (!frontmatterMatch) return null;
|
|
182
|
+
|
|
183
|
+
const frontmatter = frontmatterMatch[1];
|
|
184
|
+
const toolsMatch = frontmatter.match(/^tools:[ \t]*(\S.*)?$/m);
|
|
185
|
+
if (!toolsMatch) return null;
|
|
186
|
+
|
|
187
|
+
const tools = toolsMatch[1];
|
|
188
|
+
// Check for Write or Edit without restrictions
|
|
189
|
+
if (/\b(Write|Edit)\b(?!\s*\()/.test(tools)) {
|
|
190
|
+
return {
|
|
191
|
+
issue: 'Broad file write access',
|
|
192
|
+
fix: 'Consider restricting to specific directories',
|
|
193
|
+
line: content.substring(0, frontmatterMatch.index + frontmatterMatch[0].indexOf(toolsMatch[0])).split('\n').length
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
return null;
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Unsafe eval patterns
|
|
202
|
+
* HIGH certainty - dangerous
|
|
203
|
+
*/
|
|
204
|
+
unsafe_eval: {
|
|
205
|
+
id: 'unsafe_eval',
|
|
206
|
+
category: 'security',
|
|
207
|
+
certainty: 'HIGH',
|
|
208
|
+
autoFix: false,
|
|
209
|
+
description: 'Unsafe eval() or Function() usage',
|
|
210
|
+
pattern: /\b(?:eval|Function)\s*\(/,
|
|
211
|
+
check: (content, filePath) => {
|
|
212
|
+
const issues = [];
|
|
213
|
+
const lines = content.split('\n');
|
|
214
|
+
|
|
215
|
+
for (let i = 0; i < lines.length; i++) {
|
|
216
|
+
const line = lines[i];
|
|
217
|
+
if (/\b(?:eval|Function)\s*\(/.test(line)) {
|
|
218
|
+
issues.push({
|
|
219
|
+
issue: 'Unsafe eval() or Function() usage',
|
|
220
|
+
fix: 'Avoid dynamic code execution - use safer alternatives',
|
|
221
|
+
line: i + 1
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return issues.length > 0 ? issues : null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* Get all security patterns
|
|
233
|
+
* @returns {Object} All security patterns
|
|
234
|
+
*/
|
|
235
|
+
function getAllPatterns() {
|
|
236
|
+
return securityPatterns;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Get patterns by certainty level
|
|
241
|
+
* @param {string} certainty - HIGH, MEDIUM, or LOW
|
|
242
|
+
* @returns {Object} Filtered patterns
|
|
243
|
+
*/
|
|
244
|
+
function getPatternsByCertainty(certainty) {
|
|
245
|
+
const result = {};
|
|
246
|
+
for (const [name, pattern] of Object.entries(securityPatterns)) {
|
|
247
|
+
if (pattern.certainty === certainty) {
|
|
248
|
+
result[name] = pattern;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Run all security checks on content
|
|
256
|
+
* @param {string} content - File content
|
|
257
|
+
* @param {string} filePath - File path
|
|
258
|
+
* @returns {Array} Array of issues found
|
|
259
|
+
*/
|
|
260
|
+
function checkSecurity(content, filePath) {
|
|
261
|
+
const issues = [];
|
|
262
|
+
|
|
263
|
+
for (const [name, pattern] of Object.entries(securityPatterns)) {
|
|
264
|
+
if (pattern.check) {
|
|
265
|
+
const result = pattern.check(content, filePath);
|
|
266
|
+
if (result) {
|
|
267
|
+
if (Array.isArray(result)) {
|
|
268
|
+
issues.push(...result.map(r => ({ ...r, patternId: pattern.id, certainty: pattern.certainty })));
|
|
269
|
+
} else {
|
|
270
|
+
issues.push({ ...result, patternId: pattern.id, certainty: pattern.certainty });
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
return issues;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
module.exports = {
|
|
280
|
+
securityPatterns,
|
|
281
|
+
getAllPatterns,
|
|
282
|
+
getPatternsByCertainty,
|
|
283
|
+
checkSecurity
|
|
284
|
+
};
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill analyzer for /enhance.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const path = require('path');
|
|
7
|
+
const { skillPatterns } = require('./skill-patterns');
|
|
8
|
+
const { parseMarkdownFrontmatter } = require('./agent-analyzer');
|
|
9
|
+
|
|
10
|
+
function analyzeSkill(skillPath) {
|
|
11
|
+
const results = {
|
|
12
|
+
skillName: path.basename(path.dirname(skillPath)),
|
|
13
|
+
skillPath,
|
|
14
|
+
structureIssues: [],
|
|
15
|
+
triggerIssues: []
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
if (!fs.existsSync(skillPath)) {
|
|
19
|
+
results.structureIssues.push({
|
|
20
|
+
issue: 'File not found',
|
|
21
|
+
file: skillPath,
|
|
22
|
+
certainty: 'HIGH',
|
|
23
|
+
patternId: 'file_not_found'
|
|
24
|
+
});
|
|
25
|
+
return results;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
let content = '';
|
|
29
|
+
try {
|
|
30
|
+
content = fs.readFileSync(skillPath, 'utf8');
|
|
31
|
+
} catch (err) {
|
|
32
|
+
results.structureIssues.push({
|
|
33
|
+
issue: `Failed to read file: ${err.message}`,
|
|
34
|
+
file: skillPath,
|
|
35
|
+
certainty: 'HIGH',
|
|
36
|
+
patternId: 'read_error'
|
|
37
|
+
});
|
|
38
|
+
return results;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const missingFm = skillPatterns.missing_frontmatter.check(content);
|
|
42
|
+
if (missingFm) {
|
|
43
|
+
results.structureIssues.push({
|
|
44
|
+
...missingFm,
|
|
45
|
+
file: skillPath,
|
|
46
|
+
certainty: skillPatterns.missing_frontmatter.certainty,
|
|
47
|
+
patternId: skillPatterns.missing_frontmatter.id
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const { frontmatter } = parseMarkdownFrontmatter(content);
|
|
52
|
+
const missingName = skillPatterns.missing_name.check(frontmatter);
|
|
53
|
+
if (missingName) {
|
|
54
|
+
results.structureIssues.push({
|
|
55
|
+
...missingName,
|
|
56
|
+
file: skillPath,
|
|
57
|
+
certainty: skillPatterns.missing_name.certainty,
|
|
58
|
+
patternId: skillPatterns.missing_name.id
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const missingDescription = skillPatterns.missing_description.check(frontmatter);
|
|
63
|
+
if (missingDescription) {
|
|
64
|
+
results.structureIssues.push({
|
|
65
|
+
...missingDescription,
|
|
66
|
+
file: skillPath,
|
|
67
|
+
certainty: skillPatterns.missing_description.certainty,
|
|
68
|
+
patternId: skillPatterns.missing_description.id
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const missingTrigger = skillPatterns.missing_trigger_phrase.check(frontmatter);
|
|
73
|
+
if (missingTrigger) {
|
|
74
|
+
results.triggerIssues.push({
|
|
75
|
+
...missingTrigger,
|
|
76
|
+
file: skillPath,
|
|
77
|
+
certainty: skillPatterns.missing_trigger_phrase.certainty,
|
|
78
|
+
patternId: skillPatterns.missing_trigger_phrase.id,
|
|
79
|
+
autoFix: skillPatterns.missing_trigger_phrase.autoFix
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Check new patterns from Claude Code Best Practices
|
|
84
|
+
if (skillPatterns.side_effect_without_disable) {
|
|
85
|
+
const sideEffect = skillPatterns.side_effect_without_disable.check(frontmatter, content);
|
|
86
|
+
if (sideEffect) {
|
|
87
|
+
results.structureIssues.push({
|
|
88
|
+
...sideEffect,
|
|
89
|
+
file: skillPath,
|
|
90
|
+
certainty: skillPatterns.side_effect_without_disable.certainty,
|
|
91
|
+
patternId: skillPatterns.side_effect_without_disable.id
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (skillPatterns.missing_context_fork) {
|
|
97
|
+
const missingFork = skillPatterns.missing_context_fork.check(frontmatter, content);
|
|
98
|
+
if (missingFork) {
|
|
99
|
+
results.structureIssues.push({
|
|
100
|
+
...missingFork,
|
|
101
|
+
file: skillPath,
|
|
102
|
+
certainty: skillPatterns.missing_context_fork.certainty,
|
|
103
|
+
patternId: skillPatterns.missing_context_fork.id
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (skillPatterns.missing_allowed_tools) {
|
|
109
|
+
const missingTools = skillPatterns.missing_allowed_tools.check(frontmatter);
|
|
110
|
+
if (missingTools) {
|
|
111
|
+
results.structureIssues.push({
|
|
112
|
+
...missingTools,
|
|
113
|
+
file: skillPath,
|
|
114
|
+
certainty: skillPatterns.missing_allowed_tools.certainty,
|
|
115
|
+
patternId: skillPatterns.missing_allowed_tools.id
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return results;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function analyzeAllSkills(skillsDir) {
|
|
124
|
+
const results = [];
|
|
125
|
+
if (!fs.existsSync(skillsDir)) return results;
|
|
126
|
+
|
|
127
|
+
const skillFiles = [];
|
|
128
|
+
const skipDirs = new Set(['node_modules', '.git', 'dist', 'build', 'out', 'target']);
|
|
129
|
+
|
|
130
|
+
function walk(dir) {
|
|
131
|
+
let entries;
|
|
132
|
+
try {
|
|
133
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
134
|
+
} catch (err) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
for (const entry of entries) {
|
|
139
|
+
const fullPath = path.join(dir, entry.name);
|
|
140
|
+
if (entry.isDirectory()) {
|
|
141
|
+
if (!skipDirs.has(entry.name)) {
|
|
142
|
+
walk(fullPath);
|
|
143
|
+
}
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (entry.isFile() && entry.name === 'SKILL.md') {
|
|
148
|
+
skillFiles.push(fullPath);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
walk(skillsDir);
|
|
154
|
+
|
|
155
|
+
for (const skillPath of skillFiles) {
|
|
156
|
+
results.push(analyzeSkill(skillPath));
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return results;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function analyze(options = {}) {
|
|
163
|
+
const {
|
|
164
|
+
skill,
|
|
165
|
+
skillsDir = 'plugins/enhance/skills'
|
|
166
|
+
} = options;
|
|
167
|
+
|
|
168
|
+
if (skill) {
|
|
169
|
+
const skillPath = skill.endsWith('SKILL.md')
|
|
170
|
+
? skill
|
|
171
|
+
: path.join(skillsDir, skill, 'SKILL.md');
|
|
172
|
+
return analyzeSkill(skillPath);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
return analyzeAllSkills(skillsDir);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
module.exports = {
|
|
179
|
+
analyzeSkill,
|
|
180
|
+
analyzeAllSkills,
|
|
181
|
+
analyze
|
|
182
|
+
};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill patterns for /enhance.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
const skillPatterns = {
|
|
6
|
+
missing_frontmatter: {
|
|
7
|
+
id: 'missing_frontmatter',
|
|
8
|
+
certainty: 'HIGH',
|
|
9
|
+
check(content) {
|
|
10
|
+
if (!content || !content.trim().startsWith('---')) {
|
|
11
|
+
return { issue: 'Missing YAML frontmatter in SKILL.md' };
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
missing_name: {
|
|
17
|
+
id: 'missing_name',
|
|
18
|
+
certainty: 'HIGH',
|
|
19
|
+
check(frontmatter) {
|
|
20
|
+
if (!frontmatter || !frontmatter.name) {
|
|
21
|
+
return { issue: 'Missing name in SKILL.md frontmatter' };
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
missing_description: {
|
|
27
|
+
id: 'missing_description',
|
|
28
|
+
certainty: 'HIGH',
|
|
29
|
+
check(frontmatter) {
|
|
30
|
+
if (!frontmatter || !frontmatter.description) {
|
|
31
|
+
return { issue: 'Missing description in SKILL.md frontmatter' };
|
|
32
|
+
}
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
missing_trigger_phrase: {
|
|
37
|
+
id: 'missing_trigger_phrase',
|
|
38
|
+
certainty: 'MEDIUM',
|
|
39
|
+
autoFix: true,
|
|
40
|
+
check(frontmatter) {
|
|
41
|
+
if (!frontmatter || !frontmatter.description) return null;
|
|
42
|
+
if (!/use when user asks/i.test(frontmatter.description)) {
|
|
43
|
+
return { issue: 'Description missing "Use when user asks" trigger phrase' };
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
|
|
49
|
+
// ============================================
|
|
50
|
+
// PATTERNS FROM CLAUDE CODE BEST PRACTICES
|
|
51
|
+
// Source: https://code.claude.com/docs/en/best-practices
|
|
52
|
+
// ============================================
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Side-effect skill without disable-model-invocation
|
|
56
|
+
* HIGH certainty - skills with side effects should be manual-only
|
|
57
|
+
*/
|
|
58
|
+
side_effect_without_disable: {
|
|
59
|
+
id: 'side_effect_without_disable',
|
|
60
|
+
certainty: 'HIGH',
|
|
61
|
+
check(frontmatter, content) {
|
|
62
|
+
if (!frontmatter) return null;
|
|
63
|
+
|
|
64
|
+
// Check if skill has side effects indicators
|
|
65
|
+
const sideEffectPatterns = [
|
|
66
|
+
/\b(?:deploy|ship|push|merge|commit|delete|remove|publish)\b/i,
|
|
67
|
+
/\bgit\s+(?:push|commit|reset)\b/i,
|
|
68
|
+
/\bcreate\s+(?:PR|pull\s+request|issue)\b/i,
|
|
69
|
+
/\bsend\s+(?:email|notification|message)\b/i,
|
|
70
|
+
/\brun\s+(?:migration|deploy)\b/i
|
|
71
|
+
];
|
|
72
|
+
|
|
73
|
+
const hasSideEffects = sideEffectPatterns.some(p => {
|
|
74
|
+
return p.test(frontmatter.name || '') ||
|
|
75
|
+
p.test(frontmatter.description || '') ||
|
|
76
|
+
(content && p.test(content));
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
if (hasSideEffects && frontmatter['disable-model-invocation'] !== true) {
|
|
80
|
+
return {
|
|
81
|
+
issue: 'Skill with side effects should have disable-model-invocation: true',
|
|
82
|
+
fix: 'Add "disable-model-invocation: true" to frontmatter for manual-only invocation'
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Missing context: fork for isolated execution
|
|
91
|
+
* MEDIUM certainty - skills that read many files should fork context
|
|
92
|
+
*/
|
|
93
|
+
missing_context_fork: {
|
|
94
|
+
id: 'missing_context_fork',
|
|
95
|
+
certainty: 'MEDIUM',
|
|
96
|
+
check(frontmatter, content) {
|
|
97
|
+
if (!frontmatter) return null;
|
|
98
|
+
|
|
99
|
+
// Check if skill does extensive exploration
|
|
100
|
+
const explorationPatterns = [
|
|
101
|
+
/\b(?:search|explore|analyze|scan)\s+(?:the\s+)?(?:codebase|repo|project)\b/i,
|
|
102
|
+
/\bread\s+(?:all|many|multiple)\s+files\b/i,
|
|
103
|
+
/\b(?:deep|thorough)\s+(?:analysis|review|investigation)\b/i
|
|
104
|
+
];
|
|
105
|
+
|
|
106
|
+
const doesExploration = explorationPatterns.some(p => {
|
|
107
|
+
return p.test(frontmatter.description || '') ||
|
|
108
|
+
(content && p.test(content));
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
if (doesExploration && frontmatter.context !== 'fork') {
|
|
112
|
+
return {
|
|
113
|
+
issue: 'Exploration skill should use context: fork to keep main context clean',
|
|
114
|
+
fix: 'Add "context: fork" to run in isolated subagent context'
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Missing allowed-tools restriction
|
|
123
|
+
* MEDIUM certainty - skills should specify allowed tools
|
|
124
|
+
*/
|
|
125
|
+
missing_allowed_tools: {
|
|
126
|
+
id: 'missing_allowed_tools',
|
|
127
|
+
certainty: 'MEDIUM',
|
|
128
|
+
check(frontmatter) {
|
|
129
|
+
if (!frontmatter) return null;
|
|
130
|
+
|
|
131
|
+
// Only flag if skill has context: fork (subagent execution)
|
|
132
|
+
if (frontmatter.context !== 'fork') return null;
|
|
133
|
+
|
|
134
|
+
if (!frontmatter['allowed-tools']) {
|
|
135
|
+
return {
|
|
136
|
+
issue: 'Forked skill missing allowed-tools restriction',
|
|
137
|
+
fix: 'Add "allowed-tools: Read, Grep, Glob" to scope subagent capabilities'
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
module.exports = {
|
|
146
|
+
skillPatterns
|
|
147
|
+
};
|