awesome-slash 2.4.4 → 2.5.1
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 +6 -6
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +123 -1
- package/README.md +186 -159
- package/SECURITY.md +25 -81
- package/adapters/codex/install.sh +58 -16
- package/adapters/opencode/install.sh +92 -23
- package/lib/index.js +47 -4
- package/lib/patterns/review-patterns.js +58 -11
- package/lib/patterns/slop-patterns.js +154 -147
- package/lib/platform/detect-platform.js +99 -350
- package/lib/platform/detection-configs.js +93 -0
- package/lib/platform/state-dir.js +122 -0
- package/lib/platform/verify-tools.js +10 -78
- package/lib/schemas/README.md +195 -0
- package/lib/schemas/validator.js +247 -0
- package/lib/sources/custom-handler.js +199 -0
- package/lib/sources/policy-questions.js +239 -0
- package/lib/sources/source-cache.js +164 -0
- package/lib/state/workflow-state.js +368 -665
- package/lib/types/README.md +292 -0
- package/lib/types/agent-frontmatter.d.ts +134 -0
- package/lib/types/command-frontmatter.d.ts +107 -0
- package/lib/types/hook-frontmatter.d.ts +115 -0
- package/lib/types/index.d.ts +84 -0
- package/lib/types/plugin-manifest.d.ts +102 -0
- package/lib/types/skill-frontmatter.d.ts +89 -0
- package/lib/utils/cache-manager.js +154 -0
- package/lib/utils/context-optimizer.js +5 -36
- package/lib/utils/deprecation.js +37 -0
- package/lib/utils/shell-escape.js +88 -0
- package/mcp-server/index.js +513 -22
- package/package.json +6 -2
- package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
- package/plugins/deslop-around/lib/index.js +170 -0
- package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
- package/plugins/deslop-around/lib/patterns/slop-patterns.js +169 -129
- package/plugins/deslop-around/lib/platform/detect-platform.js +162 -316
- package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
- package/plugins/deslop-around/lib/platform/state-dir.js +122 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +10 -78
- package/plugins/deslop-around/lib/schemas/README.md +195 -0
- package/plugins/deslop-around/lib/schemas/validator.js +247 -0
- package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
- package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
- package/plugins/deslop-around/lib/sources/source-cache.js +164 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +387 -484
- package/plugins/deslop-around/lib/types/README.md +292 -0
- package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/deslop-around/lib/types/index.d.ts +84 -0
- package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
- package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
- package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
- package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
- package/plugins/next-task/.claude-plugin/plugin.json +1 -1
- package/plugins/next-task/agents/delivery-validator.md +2 -2
- package/plugins/next-task/agents/implementation-agent.md +3 -4
- package/plugins/next-task/agents/planning-agent.md +77 -19
- package/plugins/next-task/agents/review-orchestrator.md +21 -122
- package/plugins/next-task/agents/task-discoverer.md +164 -23
- package/plugins/next-task/commands/next-task.md +180 -14
- package/plugins/next-task/lib/index.js +170 -0
- package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
- package/plugins/next-task/lib/patterns/slop-patterns.js +169 -129
- package/plugins/next-task/lib/platform/detect-platform.js +162 -316
- package/plugins/next-task/lib/platform/detection-configs.js +93 -0
- package/plugins/next-task/lib/platform/state-dir.js +122 -0
- package/plugins/next-task/lib/platform/verify-tools.js +10 -78
- package/plugins/next-task/lib/schemas/README.md +195 -0
- package/plugins/next-task/lib/schemas/validator.js +247 -0
- package/plugins/next-task/lib/sources/custom-handler.js +199 -0
- package/plugins/next-task/lib/sources/policy-questions.js +239 -0
- package/plugins/next-task/lib/sources/source-cache.js +164 -0
- package/plugins/next-task/lib/state/workflow-state.js +387 -484
- package/plugins/next-task/lib/types/README.md +292 -0
- package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/next-task/lib/types/index.d.ts +84 -0
- package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/next-task/lib/utils/cache-manager.js +154 -0
- package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
- package/plugins/next-task/lib/utils/deprecation.js +37 -0
- package/plugins/next-task/lib/utils/shell-escape.js +88 -0
- package/plugins/project-review/.claude-plugin/plugin.json +1 -1
- package/plugins/project-review/lib/index.js +170 -0
- package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
- package/plugins/project-review/lib/patterns/slop-patterns.js +169 -129
- package/plugins/project-review/lib/platform/detect-platform.js +162 -316
- package/plugins/project-review/lib/platform/detection-configs.js +93 -0
- package/plugins/project-review/lib/platform/state-dir.js +122 -0
- package/plugins/project-review/lib/platform/verify-tools.js +10 -78
- package/plugins/project-review/lib/schemas/README.md +195 -0
- package/plugins/project-review/lib/schemas/validator.js +247 -0
- package/plugins/project-review/lib/sources/custom-handler.js +199 -0
- package/plugins/project-review/lib/sources/policy-questions.js +239 -0
- package/plugins/project-review/lib/sources/source-cache.js +164 -0
- package/plugins/project-review/lib/state/workflow-state.js +387 -484
- package/plugins/project-review/lib/types/README.md +292 -0
- package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/project-review/lib/types/index.d.ts +84 -0
- package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/project-review/lib/utils/cache-manager.js +154 -0
- package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
- package/plugins/project-review/lib/utils/deprecation.js +37 -0
- package/plugins/project-review/lib/utils/shell-escape.js +88 -0
- package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
- package/plugins/reality-check/agents/code-explorer.md +1 -1
- package/plugins/ship/.claude-plugin/plugin.json +1 -1
- package/plugins/ship/lib/index.js +170 -0
- package/plugins/ship/lib/patterns/review-patterns.js +58 -11
- package/plugins/ship/lib/patterns/slop-patterns.js +169 -129
- package/plugins/ship/lib/platform/detect-platform.js +162 -316
- package/plugins/ship/lib/platform/detection-configs.js +93 -0
- package/plugins/ship/lib/platform/state-dir.js +122 -0
- package/plugins/ship/lib/platform/verify-tools.js +10 -78
- package/plugins/ship/lib/schemas/README.md +195 -0
- package/plugins/ship/lib/schemas/validator.js +247 -0
- package/plugins/ship/lib/sources/custom-handler.js +199 -0
- package/plugins/ship/lib/sources/policy-questions.js +239 -0
- package/plugins/ship/lib/sources/source-cache.js +164 -0
- package/plugins/ship/lib/state/workflow-state.js +387 -484
- package/plugins/ship/lib/types/README.md +292 -0
- package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
- package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
- package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
- package/plugins/ship/lib/types/index.d.ts +84 -0
- package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
- package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
- package/plugins/ship/lib/utils/cache-manager.js +154 -0
- package/plugins/ship/lib/utils/context-optimizer.js +115 -37
- package/plugins/ship/lib/utils/deprecation.js +37 -0
- package/plugins/ship/lib/utils/shell-escape.js +88 -0
- package/scripts/install/codex.sh +216 -72
- package/scripts/install/opencode.sh +197 -21
- package/lib/state/workflow-state.schema.json +0 -282
- package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/agents/policy-selector.md +0 -248
- package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
- package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
- package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
- package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
- package/plugins/ship/lib/state/workflow-state.schema.json +0 -282
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detection Configurations
|
|
3
|
+
* Centralized config for platform detection logic
|
|
4
|
+
*
|
|
5
|
+
* @module lib/platform/detection-configs
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* CI platform detection configuration
|
|
12
|
+
* Order matters - first match wins
|
|
13
|
+
*/
|
|
14
|
+
const CI_CONFIGS = [
|
|
15
|
+
{ file: '.github/workflows', platform: 'github-actions' },
|
|
16
|
+
{ file: '.gitlab-ci.yml', platform: 'gitlab-ci' },
|
|
17
|
+
{ file: '.circleci/config.yml', platform: 'circleci' },
|
|
18
|
+
{ file: 'Jenkinsfile', platform: 'jenkins' },
|
|
19
|
+
{ file: '.travis.yml', platform: 'travis' }
|
|
20
|
+
];
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Deployment platform detection configuration
|
|
24
|
+
* Order matters - first match wins
|
|
25
|
+
*/
|
|
26
|
+
const DEPLOYMENT_CONFIGS = [
|
|
27
|
+
{ file: 'railway.json', platform: 'railway' },
|
|
28
|
+
{ file: 'railway.toml', platform: 'railway' }, // Legacy Railway marker
|
|
29
|
+
{ file: 'vercel.json', platform: 'vercel' },
|
|
30
|
+
{ file: 'netlify.toml', platform: 'netlify' },
|
|
31
|
+
{ file: '.netlify', platform: 'netlify' }, // Legacy Netlify marker
|
|
32
|
+
{ file: 'fly.toml', platform: 'fly' },
|
|
33
|
+
{ file: '.platform.app.yaml', platform: 'platformsh' },
|
|
34
|
+
{ file: 'render.yaml', platform: 'render' }
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Project type detection configuration
|
|
39
|
+
* Checks package.json for framework indicators
|
|
40
|
+
*/
|
|
41
|
+
const PROJECT_TYPE_CONFIGS = {
|
|
42
|
+
dependencies: [
|
|
43
|
+
{ name: 'next', type: 'nextjs' },
|
|
44
|
+
{ name: 'react', type: 'react' },
|
|
45
|
+
{ name: 'vue', type: 'vue' },
|
|
46
|
+
{ name: '@angular/core', type: 'angular' },
|
|
47
|
+
{ name: 'svelte', type: 'svelte' },
|
|
48
|
+
{ name: 'express', type: 'express' },
|
|
49
|
+
{ name: '@nestjs/core', type: 'nestjs' },
|
|
50
|
+
{ name: 'gatsby', type: 'gatsby' },
|
|
51
|
+
{ name: '@remix-run/react', type: 'remix' },
|
|
52
|
+
{ name: 'astro', type: 'astro' }
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Package manager detection configuration
|
|
58
|
+
* Lock files indicate package manager used
|
|
59
|
+
* Order matters - first match wins (prioritize pnpm > yarn > bun > npm for Node.js)
|
|
60
|
+
*/
|
|
61
|
+
const PACKAGE_MANAGER_CONFIGS = [
|
|
62
|
+
{ file: 'pnpm-lock.yaml', manager: 'pnpm' },
|
|
63
|
+
{ file: 'yarn.lock', manager: 'yarn' },
|
|
64
|
+
{ file: 'bun.lockb', manager: 'bun' },
|
|
65
|
+
{ file: 'package-lock.json', manager: 'npm' },
|
|
66
|
+
{ file: 'poetry.lock', manager: 'poetry' },
|
|
67
|
+
{ file: 'Pipfile.lock', manager: 'pipenv' },
|
|
68
|
+
{ file: 'Cargo.lock', manager: 'cargo' },
|
|
69
|
+
{ file: 'go.sum', manager: 'go' }
|
|
70
|
+
];
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Branch strategy patterns
|
|
74
|
+
*/
|
|
75
|
+
const BRANCH_STRATEGIES = {
|
|
76
|
+
gitflow: ['develop', 'main', 'master'],
|
|
77
|
+
githubflow: ['main'],
|
|
78
|
+
trunkbased: ['main', 'trunk']
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Main branch candidates (in priority order)
|
|
83
|
+
*/
|
|
84
|
+
const MAIN_BRANCH_CANDIDATES = ['main', 'master', 'trunk', 'develop'];
|
|
85
|
+
|
|
86
|
+
module.exports = {
|
|
87
|
+
CI_CONFIGS,
|
|
88
|
+
DEPLOYMENT_CONFIGS,
|
|
89
|
+
PROJECT_TYPE_CONFIGS,
|
|
90
|
+
PACKAGE_MANAGER_CONFIGS,
|
|
91
|
+
BRANCH_STRATEGIES,
|
|
92
|
+
MAIN_BRANCH_CANDIDATES
|
|
93
|
+
};
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Platform-aware state directory detection
|
|
3
|
+
*
|
|
4
|
+
* Determines the appropriate state directory based on the AI coding assistant
|
|
5
|
+
* being used (Claude Code, OpenCode, or Codex CLI).
|
|
6
|
+
*
|
|
7
|
+
* @module lib/platform/state-dir
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Cached state directory name (relative, without leading dot handling)
|
|
15
|
+
* @type {string|null}
|
|
16
|
+
*/
|
|
17
|
+
let _cachedStateDir = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Detect which AI coding assistant is running and return appropriate state directory
|
|
21
|
+
*
|
|
22
|
+
* Detection order:
|
|
23
|
+
* 1. AI_STATE_DIR env var (user override)
|
|
24
|
+
* 2. OpenCode detection (OPENCODE_CONFIG env or .opencode/ exists)
|
|
25
|
+
* 3. Codex detection (CODEX_HOME env or .codex/ exists)
|
|
26
|
+
* 4. Default to .claude (Claude Code or unknown)
|
|
27
|
+
*
|
|
28
|
+
* @param {string} [basePath=process.cwd()] - Base path to check for project directories
|
|
29
|
+
* @returns {string} State directory name (e.g., '.claude', '.opencode', '.codex')
|
|
30
|
+
*/
|
|
31
|
+
function getStateDir(basePath = process.cwd()) {
|
|
32
|
+
// Check user override first
|
|
33
|
+
if (process.env.AI_STATE_DIR) {
|
|
34
|
+
return process.env.AI_STATE_DIR;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Return cached value if available
|
|
38
|
+
if (_cachedStateDir) {
|
|
39
|
+
return _cachedStateDir;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// OpenCode detection
|
|
43
|
+
if (process.env.OPENCODE_CONFIG || process.env.OPENCODE_CONFIG_DIR) {
|
|
44
|
+
_cachedStateDir = '.opencode';
|
|
45
|
+
return _cachedStateDir;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Check for .opencode directory in project
|
|
49
|
+
try {
|
|
50
|
+
const opencodePath = path.join(basePath, '.opencode');
|
|
51
|
+
if (fs.existsSync(opencodePath) && fs.statSync(opencodePath).isDirectory()) {
|
|
52
|
+
_cachedStateDir = '.opencode';
|
|
53
|
+
return _cachedStateDir;
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// Ignore errors, continue detection
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// Codex detection
|
|
60
|
+
if (process.env.CODEX_HOME) {
|
|
61
|
+
_cachedStateDir = '.codex';
|
|
62
|
+
return _cachedStateDir;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Check for .codex directory in project
|
|
66
|
+
try {
|
|
67
|
+
const codexPath = path.join(basePath, '.codex');
|
|
68
|
+
if (fs.existsSync(codexPath) && fs.statSync(codexPath).isDirectory()) {
|
|
69
|
+
_cachedStateDir = '.codex';
|
|
70
|
+
return _cachedStateDir;
|
|
71
|
+
}
|
|
72
|
+
} catch {
|
|
73
|
+
// Ignore errors, continue detection
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Default to Claude Code
|
|
77
|
+
_cachedStateDir = '.claude';
|
|
78
|
+
return _cachedStateDir;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get the full path to the state directory
|
|
83
|
+
* @param {string} [basePath=process.cwd()] - Base path
|
|
84
|
+
* @returns {string} Full path to state directory
|
|
85
|
+
*/
|
|
86
|
+
function getStateDirPath(basePath = process.cwd()) {
|
|
87
|
+
return path.join(basePath, getStateDir(basePath));
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get the detected platform name
|
|
92
|
+
* @param {string} [basePath=process.cwd()] - Base path
|
|
93
|
+
* @returns {string} Platform name ('claude', 'opencode', 'codex', or 'custom')
|
|
94
|
+
*/
|
|
95
|
+
function getPlatformName(basePath = process.cwd()) {
|
|
96
|
+
const stateDir = getStateDir(basePath);
|
|
97
|
+
|
|
98
|
+
if (process.env.AI_STATE_DIR) {
|
|
99
|
+
return 'custom';
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
switch (stateDir) {
|
|
103
|
+
case '.opencode': return 'opencode';
|
|
104
|
+
case '.codex': return 'codex';
|
|
105
|
+
case '.claude': return 'claude';
|
|
106
|
+
default: return 'unknown';
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Clear the cached state directory (useful for testing)
|
|
112
|
+
*/
|
|
113
|
+
function clearCache() {
|
|
114
|
+
_cachedStateDir = null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
module.exports = {
|
|
118
|
+
getStateDir,
|
|
119
|
+
getStateDirPath,
|
|
120
|
+
getPlatformName,
|
|
121
|
+
clearCache
|
|
122
|
+
};
|
|
@@ -10,70 +10,19 @@
|
|
|
10
10
|
* @license MIT
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
const {
|
|
13
|
+
const { spawn } = require('child_process');
|
|
14
14
|
|
|
15
15
|
// Detect Windows platform
|
|
16
16
|
const isWindows = process.platform === 'win32';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
|
-
* Checks if a tool is available and returns its version
|
|
20
|
-
* Uses safe execution methods to avoid shell injection vulnerabilities
|
|
21
|
-
* @param {string} command - Command to check (e.g., 'git', 'node')
|
|
22
|
-
* @param {string} versionFlag - Flag to get version (default: '--version')
|
|
23
|
-
* @returns {Object} { available: boolean, version: string|null }
|
|
24
|
-
*/
|
|
25
|
-
function checkTool(command, versionFlag = '--version') {
|
|
26
|
-
// Validate command contains only safe characters (alphanumeric, underscore, hyphen)
|
|
27
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(command)) {
|
|
28
|
-
return { available: false, version: null };
|
|
29
|
-
}
|
|
30
|
-
// Validate versionFlag contains only safe characters
|
|
31
|
-
if (!/^[a-zA-Z0-9_-]+$/.test(versionFlag)) {
|
|
32
|
-
return { available: false, version: null };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
try {
|
|
36
|
-
let output;
|
|
37
|
-
|
|
38
|
-
if (isWindows) {
|
|
39
|
-
// On Windows, use spawnSync with shell to handle .cmd/.bat scripts
|
|
40
|
-
// Input is validated above so this is safe
|
|
41
|
-
const result = spawnSync(command, [versionFlag], {
|
|
42
|
-
encoding: 'utf8',
|
|
43
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
44
|
-
timeout: 5000,
|
|
45
|
-
windowsHide: true,
|
|
46
|
-
shell: true
|
|
47
|
-
});
|
|
48
|
-
if (result.error || result.status !== 0) {
|
|
49
|
-
return { available: false, version: null };
|
|
50
|
-
}
|
|
51
|
-
output = (result.stdout || '').trim();
|
|
52
|
-
} else {
|
|
53
|
-
// On Unix, use execFileSync (more secure, no shell)
|
|
54
|
-
output = execFileSync(command, [versionFlag], {
|
|
55
|
-
encoding: 'utf8',
|
|
56
|
-
stdio: ['pipe', 'pipe', 'ignore'],
|
|
57
|
-
timeout: 5000
|
|
58
|
-
}).trim();
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Extract version from first line
|
|
62
|
-
const version = output.split('\n')[0];
|
|
63
|
-
return { available: true, version };
|
|
64
|
-
} catch {
|
|
65
|
-
return { available: false, version: null };
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
/**
|
|
70
|
-
* Checks if a tool is available and returns its version (async)
|
|
19
|
+
* Checks if a tool is available and returns its version
|
|
71
20
|
* Uses safe execution methods to avoid shell injection vulnerabilities
|
|
72
21
|
* @param {string} command - Command to check (e.g., 'git', 'node')
|
|
73
22
|
* @param {string} versionFlag - Flag to get version (default: '--version')
|
|
74
23
|
* @returns {Promise<Object>} { available: boolean, version: string|null }
|
|
75
24
|
*/
|
|
76
|
-
function
|
|
25
|
+
function checkTool(command, versionFlag = '--version') {
|
|
77
26
|
return new Promise((resolve) => {
|
|
78
27
|
// Validate command contains only safe characters (alphanumeric, underscore, hyphen)
|
|
79
28
|
if (!/^[a-zA-Z0-9_-]+$/.test(command)) {
|
|
@@ -87,8 +36,7 @@ function checkToolAsync(command, versionFlag = '--version') {
|
|
|
87
36
|
let child;
|
|
88
37
|
|
|
89
38
|
if (isWindows) {
|
|
90
|
-
// On Windows, spawn shell directly with command as single argument
|
|
91
|
-
// Input is validated above so this is safe
|
|
39
|
+
// On Windows, spawn shell directly with command as single argument
|
|
92
40
|
child = spawn('cmd.exe', ['/c', command, versionFlag], {
|
|
93
41
|
stdio: ['pipe', 'pipe', 'ignore'],
|
|
94
42
|
windowsHide: true
|
|
@@ -178,29 +126,15 @@ const TOOL_DEFINITIONS = [
|
|
|
178
126
|
];
|
|
179
127
|
|
|
180
128
|
/**
|
|
181
|
-
* Verifies all development tools (
|
|
182
|
-
*
|
|
183
|
-
*/
|
|
184
|
-
function verifyTools() {
|
|
185
|
-
const result = {};
|
|
186
|
-
for (const tool of TOOL_DEFINITIONS) {
|
|
187
|
-
result[tool.name] = checkTool(tool.name, tool.flag);
|
|
188
|
-
}
|
|
189
|
-
return result;
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Verifies all development tools (async, parallel)
|
|
194
|
-
* Runs all tool checks in parallel for ~10x faster execution
|
|
129
|
+
* Verifies all development tools (parallel execution)
|
|
130
|
+
* Runs all tool checks in parallel for fast execution
|
|
195
131
|
* @returns {Promise<Object>} Tool availability map
|
|
196
132
|
*/
|
|
197
|
-
async function
|
|
198
|
-
// Run all checks in parallel
|
|
133
|
+
async function verifyTools() {
|
|
199
134
|
const results = await Promise.all(
|
|
200
|
-
TOOL_DEFINITIONS.map(tool =>
|
|
135
|
+
TOOL_DEFINITIONS.map(tool => checkTool(tool.name, tool.flag))
|
|
201
136
|
);
|
|
202
137
|
|
|
203
|
-
// Build result object
|
|
204
138
|
const toolMap = {};
|
|
205
139
|
TOOL_DEFINITIONS.forEach((tool, index) => {
|
|
206
140
|
toolMap[tool.name] = results[index];
|
|
@@ -209,11 +143,11 @@ async function verifyToolsAsync() {
|
|
|
209
143
|
return toolMap;
|
|
210
144
|
}
|
|
211
145
|
|
|
212
|
-
// When run directly, output JSON
|
|
146
|
+
// When run directly, output JSON
|
|
213
147
|
if (require.main === module) {
|
|
214
148
|
(async () => {
|
|
215
149
|
try {
|
|
216
|
-
const result = await
|
|
150
|
+
const result = await verifyTools();
|
|
217
151
|
console.log(JSON.stringify(result, null, 2));
|
|
218
152
|
} catch (error) {
|
|
219
153
|
console.error(JSON.stringify({
|
|
@@ -228,8 +162,6 @@ if (require.main === module) {
|
|
|
228
162
|
// Export for use as module
|
|
229
163
|
module.exports = {
|
|
230
164
|
verifyTools,
|
|
231
|
-
verifyToolsAsync,
|
|
232
165
|
checkTool,
|
|
233
|
-
checkToolAsync,
|
|
234
166
|
TOOL_DEFINITIONS
|
|
235
167
|
};
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
# JSON Schema Definitions
|
|
2
|
+
|
|
3
|
+
JSON Schema validation for plugin manifests and configuration files.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
This directory contains JSON Schema definitions and validators for:
|
|
8
|
+
|
|
9
|
+
- **Plugin Manifest** (`plugin.json`) - Plugin metadata validation
|
|
10
|
+
- Additional schemas can be added for other JSON config files
|
|
11
|
+
|
|
12
|
+
## Files
|
|
13
|
+
|
|
14
|
+
- `plugin-manifest.schema.json` - JSON Schema for plugin.json
|
|
15
|
+
- `validator.js` - Schema validation utility
|
|
16
|
+
- `README.md` - This file
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
### Command Line
|
|
21
|
+
|
|
22
|
+
Validate a plugin manifest:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# Validate default location
|
|
26
|
+
node lib/schemas/validator.js
|
|
27
|
+
|
|
28
|
+
# Validate specific file
|
|
29
|
+
node lib/schemas/validator.js plugins/next-task/.claude-plugin/plugin.json
|
|
30
|
+
|
|
31
|
+
# Output on success:
|
|
32
|
+
# ✓ Manifest is valid
|
|
33
|
+
# Plugin: next-task v2.3.1
|
|
34
|
+
# Author: Avi Fenesh
|
|
35
|
+
|
|
36
|
+
# Output on failure:
|
|
37
|
+
# ✗ Manifest is invalid:
|
|
38
|
+
# - Missing required property: name
|
|
39
|
+
# - version: does not match pattern
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### Programmatic Use
|
|
43
|
+
|
|
44
|
+
```javascript
|
|
45
|
+
const { validateManifestFile, SchemaValidator } = require('./lib/schemas/validator');
|
|
46
|
+
|
|
47
|
+
// Validate a manifest file
|
|
48
|
+
const result = validateManifestFile('.claude-plugin/plugin.json');
|
|
49
|
+
if (result.valid) {
|
|
50
|
+
console.log('Valid!', result.manifest);
|
|
51
|
+
} else {
|
|
52
|
+
console.error('Errors:', result.errors);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate manifest object directly
|
|
56
|
+
const manifest = {
|
|
57
|
+
name: "my-plugin",
|
|
58
|
+
version: "1.0.0",
|
|
59
|
+
description: "My awesome plugin",
|
|
60
|
+
author: { name: "John Doe" },
|
|
61
|
+
license: "MIT"
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const validation = SchemaValidator.validatePluginManifest(manifest);
|
|
65
|
+
if (!validation.valid) {
|
|
66
|
+
console.error(validation.errors);
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Integration in Tests
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
const { validateManifestFile } = require('./lib/schemas/validator');
|
|
74
|
+
const path = require('path');
|
|
75
|
+
|
|
76
|
+
describe('Plugin Manifest', () => {
|
|
77
|
+
it('should be valid', () => {
|
|
78
|
+
const result = validateManifestFile(
|
|
79
|
+
path.join(__dirname, '../.claude-plugin/plugin.json')
|
|
80
|
+
);
|
|
81
|
+
expect(result.valid).toBe(true);
|
|
82
|
+
expect(result.errors).toHaveLength(0);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('should have required fields', () => {
|
|
86
|
+
const result = validateManifestFile(
|
|
87
|
+
path.join(__dirname, '../.claude-plugin/plugin.json')
|
|
88
|
+
);
|
|
89
|
+
expect(result.manifest.name).toBeTruthy();
|
|
90
|
+
expect(result.manifest.version).toMatch(/^\d+\.\d+\.\d+$/);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Schema Details
|
|
96
|
+
|
|
97
|
+
### plugin-manifest.schema.json
|
|
98
|
+
|
|
99
|
+
Validates `plugin.json` files with the following rules:
|
|
100
|
+
|
|
101
|
+
**Required fields:**
|
|
102
|
+
- `name` - Kebab-case identifier (e.g., "awesome-slash")
|
|
103
|
+
- `version` - Semantic version (e.g., "1.0.0")
|
|
104
|
+
- `description` - 10-500 characters
|
|
105
|
+
- `author` - Object with `name` field
|
|
106
|
+
- `license` - SPDX identifier (e.g., "MIT")
|
|
107
|
+
|
|
108
|
+
**Optional fields:**
|
|
109
|
+
- `homepage` - URL to plugin homepage
|
|
110
|
+
- `repository` - URL to source repository
|
|
111
|
+
- `keywords` - Array of search terms (1-20 items)
|
|
112
|
+
- `minClaudeVersion` - Minimum required version
|
|
113
|
+
- `dependencies` - Plugin dependencies
|
|
114
|
+
- `config` - Plugin configuration
|
|
115
|
+
|
|
116
|
+
**Constraints:**
|
|
117
|
+
- `name` must be lowercase, kebab-case
|
|
118
|
+
- `version` must follow semver (X.Y.Z)
|
|
119
|
+
- `keywords` must be unique, 2-50 chars each
|
|
120
|
+
- `author.email` must be valid email format
|
|
121
|
+
- `author.url` must be valid URI format
|
|
122
|
+
|
|
123
|
+
### Example Valid Manifest
|
|
124
|
+
|
|
125
|
+
```json
|
|
126
|
+
{
|
|
127
|
+
"name": "awesome-slash",
|
|
128
|
+
"version": "2.4.2",
|
|
129
|
+
"description": "Professional-grade slash commands for Claude Code",
|
|
130
|
+
"author": {
|
|
131
|
+
"name": "Avi Fenesh",
|
|
132
|
+
"email": "[email protected]",
|
|
133
|
+
"url": "https://github.com/avifenesh"
|
|
134
|
+
},
|
|
135
|
+
"homepage": "https://github.com/avifenesh/awesome-slash",
|
|
136
|
+
"repository": "https://github.com/avifenesh/awesome-slash",
|
|
137
|
+
"license": "MIT",
|
|
138
|
+
"keywords": ["workflow", "automation", "productivity"],
|
|
139
|
+
"minClaudeVersion": "1.0.0"
|
|
140
|
+
}
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Validation Errors
|
|
144
|
+
|
|
145
|
+
Common validation errors and fixes:
|
|
146
|
+
|
|
147
|
+
### "Missing required property: name"
|
|
148
|
+
**Fix:** Add `name` field to plugin.json
|
|
149
|
+
|
|
150
|
+
### "name: does not match pattern"
|
|
151
|
+
**Fix:** Use lowercase, kebab-case (e.g., "my-plugin" not "MyPlugin")
|
|
152
|
+
|
|
153
|
+
### "version: does not match pattern"
|
|
154
|
+
**Fix:** Use semantic version format: "1.0.0" not "v1.0" or "1.0"
|
|
155
|
+
|
|
156
|
+
### "description: string too short"
|
|
157
|
+
**Fix:** Provide description with at least 10 characters
|
|
158
|
+
|
|
159
|
+
### "Unexpected property: xyz"
|
|
160
|
+
**Fix:** Remove invalid field or update schema if it's a new field
|
|
161
|
+
|
|
162
|
+
### "keywords: array too long"
|
|
163
|
+
**Fix:** Use maximum 20 keywords
|
|
164
|
+
|
|
165
|
+
## Adding New Schemas
|
|
166
|
+
|
|
167
|
+
To add validation for other JSON files:
|
|
168
|
+
|
|
169
|
+
1. Create `{name}.schema.json` in this directory
|
|
170
|
+
2. Add validator method to `validator.js`:
|
|
171
|
+
```javascript
|
|
172
|
+
static validate{Name}(data) {
|
|
173
|
+
const schema = this.loadSchema(path.join(__dirname, '{name}.schema.json'));
|
|
174
|
+
return this.validate(data, schema);
|
|
175
|
+
}
|
|
176
|
+
```
|
|
177
|
+
3. Export validation function
|
|
178
|
+
4. Update this README
|
|
179
|
+
|
|
180
|
+
## External Tools
|
|
181
|
+
|
|
182
|
+
For more advanced validation, consider using:
|
|
183
|
+
|
|
184
|
+
- [ajv](https://ajv.js.org/) - Production-grade JSON Schema validator
|
|
185
|
+
- [json-schema-validator](https://www.jsonschemavalidator.net/) - Online validator
|
|
186
|
+
- [VSCode JSON Schema](https://code.visualstudio.com/docs/languages/json#_json-schemas) - IDE integration
|
|
187
|
+
|
|
188
|
+
## Related
|
|
189
|
+
|
|
190
|
+
- **TypeScript Types**: `lib/types/` - TypeScript definitions for same structures
|
|
191
|
+
- **Plugin Manifest Spec**: See `lib/types/README.md` for detailed type documentation
|
|
192
|
+
|
|
193
|
+
## License
|
|
194
|
+
|
|
195
|
+
MIT © Avi Fenesh
|