awesome-slash 2.4.3 → 2.5.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 +6 -6
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +99 -1
- package/README.md +173 -161
- 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/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 +149 -0
- package/lib/state/workflow-state.js +363 -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 -18
- 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 +170 -129
- package/plugins/deslop-around/lib/platform/detect-platform.js +212 -123
- package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
- package/plugins/deslop-around/lib/platform/verify-tools.js +10 -1
- package/plugins/deslop-around/lib/schemas/README.md +195 -0
- package/plugins/deslop-around/lib/schemas/validator.js +205 -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 +149 -0
- package/plugins/deslop-around/lib/state/workflow-state.js +382 -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/ci-monitor.md +19 -0
- 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 +170 -129
- package/plugins/next-task/lib/platform/detect-platform.js +212 -123
- package/plugins/next-task/lib/platform/detection-configs.js +93 -0
- package/plugins/next-task/lib/platform/verify-tools.js +10 -1
- package/plugins/next-task/lib/schemas/README.md +195 -0
- package/plugins/next-task/lib/schemas/validator.js +205 -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 +149 -0
- package/plugins/next-task/lib/state/workflow-state.js +382 -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 +170 -129
- package/plugins/project-review/lib/platform/detect-platform.js +212 -123
- package/plugins/project-review/lib/platform/detection-configs.js +93 -0
- package/plugins/project-review/lib/platform/verify-tools.js +10 -1
- package/plugins/project-review/lib/schemas/README.md +195 -0
- package/plugins/project-review/lib/schemas/validator.js +205 -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 +149 -0
- package/plugins/project-review/lib/state/workflow-state.js +382 -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/commands/ship-ci-review-loop.md +19 -0
- 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 +170 -129
- package/plugins/ship/lib/platform/detect-platform.js +212 -123
- package/plugins/ship/lib/platform/detection-configs.js +93 -0
- package/plugins/ship/lib/platform/verify-tools.js +10 -1
- package/plugins/ship/lib/schemas/README.md +195 -0
- package/plugins/ship/lib/schemas/validator.js +205 -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 +149 -0
- package/plugins/ship/lib/state/workflow-state.js +382 -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/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,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Interface Type Definitions
|
|
3
|
+
* Centralized type definitions for all plugin components
|
|
4
|
+
*
|
|
5
|
+
* @module lib/types
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
// Re-export all types
|
|
11
|
+
export * from './plugin-manifest';
|
|
12
|
+
export * from './command-frontmatter';
|
|
13
|
+
export * from './agent-frontmatter';
|
|
14
|
+
export * from './skill-frontmatter';
|
|
15
|
+
export * from './hook-frontmatter';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Plugin component types union
|
|
19
|
+
*/
|
|
20
|
+
export type PluginComponentType = 'command' | 'agent' | 'skill' | 'hook';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Plugin directory structure
|
|
24
|
+
*/
|
|
25
|
+
export interface PluginStructure {
|
|
26
|
+
/** Plugin root directory */
|
|
27
|
+
root: string;
|
|
28
|
+
|
|
29
|
+
/** Plugin manifest (plugin.json) */
|
|
30
|
+
manifest: string;
|
|
31
|
+
|
|
32
|
+
/** Commands directory */
|
|
33
|
+
commands?: string;
|
|
34
|
+
|
|
35
|
+
/** Agents directory */
|
|
36
|
+
agents?: string;
|
|
37
|
+
|
|
38
|
+
/** Skills directory */
|
|
39
|
+
skills?: string;
|
|
40
|
+
|
|
41
|
+
/** Hooks directory */
|
|
42
|
+
hooks?: string;
|
|
43
|
+
|
|
44
|
+
/** Shared library directory */
|
|
45
|
+
lib?: string;
|
|
46
|
+
|
|
47
|
+
/** Tests directory */
|
|
48
|
+
tests?: string;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Plugin validation result
|
|
53
|
+
*/
|
|
54
|
+
export interface PluginValidationResult {
|
|
55
|
+
/** Whether plugin is valid */
|
|
56
|
+
valid: boolean;
|
|
57
|
+
|
|
58
|
+
/** Validation errors (if any) */
|
|
59
|
+
errors: string[];
|
|
60
|
+
|
|
61
|
+
/** Validation warnings (if any) */
|
|
62
|
+
warnings: string[];
|
|
63
|
+
|
|
64
|
+
/** Detected components */
|
|
65
|
+
components: {
|
|
66
|
+
commands: number;
|
|
67
|
+
agents: number;
|
|
68
|
+
skills: number;
|
|
69
|
+
hooks: number;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Standard plugin directory structure
|
|
75
|
+
*/
|
|
76
|
+
export const PLUGIN_STRUCTURE: Readonly<Record<string, string>> = {
|
|
77
|
+
MANIFEST: '.claude-plugin/plugin.json',
|
|
78
|
+
COMMANDS: 'commands',
|
|
79
|
+
AGENTS: 'agents',
|
|
80
|
+
SKILLS: 'skills',
|
|
81
|
+
HOOKS: 'hooks',
|
|
82
|
+
LIB: 'lib',
|
|
83
|
+
TESTS: 'tests'
|
|
84
|
+
} as const;
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin Manifest Type Definitions
|
|
3
|
+
* Defines the structure of plugin.json files
|
|
4
|
+
*
|
|
5
|
+
* @module lib/types/plugin-manifest
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Author information for plugin manifest
|
|
12
|
+
*/
|
|
13
|
+
export interface PluginAuthor {
|
|
14
|
+
/** Author's full name */
|
|
15
|
+
name: string;
|
|
16
|
+
/** Author's email address (optional) */
|
|
17
|
+
email?: string;
|
|
18
|
+
/** Author's website or GitHub profile URL (optional) */
|
|
19
|
+
url?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Plugin manifest structure
|
|
24
|
+
* Required fields for all Claude Code plugins
|
|
25
|
+
*/
|
|
26
|
+
export interface PluginManifest {
|
|
27
|
+
/** Unique plugin identifier (kebab-case, lowercase) */
|
|
28
|
+
name: string;
|
|
29
|
+
|
|
30
|
+
/** Semantic version (MAJOR.MINOR.PATCH) */
|
|
31
|
+
version: string;
|
|
32
|
+
|
|
33
|
+
/** Short description of plugin functionality */
|
|
34
|
+
description: string;
|
|
35
|
+
|
|
36
|
+
/** Author information */
|
|
37
|
+
author: PluginAuthor;
|
|
38
|
+
|
|
39
|
+
/** Plugin homepage URL (optional) */
|
|
40
|
+
homepage?: string;
|
|
41
|
+
|
|
42
|
+
/** Repository URL (optional) */
|
|
43
|
+
repository?: string;
|
|
44
|
+
|
|
45
|
+
/** License identifier (SPDX format, e.g., "MIT", "Apache-2.0") */
|
|
46
|
+
license: string;
|
|
47
|
+
|
|
48
|
+
/** Search keywords for discoverability (optional) */
|
|
49
|
+
keywords?: string[];
|
|
50
|
+
|
|
51
|
+
/** Minimum Claude Code version required (optional) */
|
|
52
|
+
minClaudeVersion?: string;
|
|
53
|
+
|
|
54
|
+
/** Plugin dependencies (optional) */
|
|
55
|
+
dependencies?: {
|
|
56
|
+
[pluginName: string]: string; // version constraint
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
/** Plugin configuration schema (optional) */
|
|
60
|
+
config?: {
|
|
61
|
+
[key: string]: unknown;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Type guard to check if an object is a valid PluginManifest
|
|
67
|
+
*/
|
|
68
|
+
export function isPluginManifest(obj: unknown): obj is PluginManifest {
|
|
69
|
+
if (typeof obj !== 'object' || obj === null) return false;
|
|
70
|
+
const manifest = obj as Partial<PluginManifest>;
|
|
71
|
+
|
|
72
|
+
return (
|
|
73
|
+
typeof manifest.name === 'string' &&
|
|
74
|
+
/^[a-z0-9-]+$/.test(manifest.name) &&
|
|
75
|
+
typeof manifest.version === 'string' &&
|
|
76
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/.test(manifest.version) &&
|
|
77
|
+
typeof manifest.description === 'string' &&
|
|
78
|
+
typeof manifest.author === 'object' &&
|
|
79
|
+
manifest.author !== null &&
|
|
80
|
+
typeof manifest.author.name === 'string' &&
|
|
81
|
+
typeof manifest.license === 'string'
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Validates a plugin manifest against the schema
|
|
87
|
+
* @throws {Error} If manifest is invalid
|
|
88
|
+
*/
|
|
89
|
+
export function validatePluginManifest(manifest: unknown): asserts manifest is PluginManifest {
|
|
90
|
+
if (!isPluginManifest(manifest)) {
|
|
91
|
+
throw new Error('Invalid plugin manifest: missing required fields or invalid format');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Additional validations
|
|
95
|
+
if (manifest.keywords && !Array.isArray(manifest.keywords)) {
|
|
96
|
+
throw new Error('Invalid plugin manifest: keywords must be an array');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (manifest.dependencies && typeof manifest.dependencies !== 'object') {
|
|
100
|
+
throw new Error('Invalid plugin manifest: dependencies must be an object');
|
|
101
|
+
}
|
|
102
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Skill Frontmatter Type Definitions
|
|
3
|
+
* Defines the structure of YAML frontmatter in skill markdown files
|
|
4
|
+
*
|
|
5
|
+
* @module lib/types/skill-frontmatter
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Skill frontmatter structure
|
|
12
|
+
* YAML metadata at the top of skill markdown files
|
|
13
|
+
*/
|
|
14
|
+
export interface SkillFrontmatter {
|
|
15
|
+
/** Skill unique identifier (kebab-case) */
|
|
16
|
+
skill: string;
|
|
17
|
+
|
|
18
|
+
/** Short description of skill purpose */
|
|
19
|
+
description: string;
|
|
20
|
+
|
|
21
|
+
/** Skill category for organization */
|
|
22
|
+
category?: string;
|
|
23
|
+
|
|
24
|
+
/** When this skill should be invoked (triggering conditions) */
|
|
25
|
+
'when-to-use'?: string[];
|
|
26
|
+
|
|
27
|
+
/** Example usage scenarios */
|
|
28
|
+
examples?: string[];
|
|
29
|
+
|
|
30
|
+
/** Preferred model for this skill (sonnet, opus, haiku) */
|
|
31
|
+
model?: 'sonnet' | 'opus' | 'haiku';
|
|
32
|
+
|
|
33
|
+
/** Whether skill requires user approval before running */
|
|
34
|
+
requiresApproval?: boolean;
|
|
35
|
+
|
|
36
|
+
/** Tags for searchability */
|
|
37
|
+
tags?: string[];
|
|
38
|
+
|
|
39
|
+
/** Related commands or skills */
|
|
40
|
+
related?: string[];
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Type guard to check if an object is valid SkillFrontmatter
|
|
45
|
+
*/
|
|
46
|
+
export function isSkillFrontmatter(obj: unknown): obj is SkillFrontmatter {
|
|
47
|
+
if (typeof obj !== 'object' || obj === null) return false;
|
|
48
|
+
const fm = obj as Partial<SkillFrontmatter>;
|
|
49
|
+
|
|
50
|
+
return (
|
|
51
|
+
typeof fm.skill === 'string' &&
|
|
52
|
+
fm.skill.length > 0 &&
|
|
53
|
+
typeof fm.description === 'string' &&
|
|
54
|
+
fm.description.length > 0
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validates skill frontmatter
|
|
60
|
+
* @throws {Error} If frontmatter is invalid
|
|
61
|
+
*/
|
|
62
|
+
export function validateSkillFrontmatter(
|
|
63
|
+
frontmatter: unknown
|
|
64
|
+
): asserts frontmatter is SkillFrontmatter {
|
|
65
|
+
if (!isSkillFrontmatter(frontmatter)) {
|
|
66
|
+
throw new Error('Invalid skill frontmatter: missing required fields');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Additional validations
|
|
70
|
+
if (frontmatter.model && !['sonnet', 'opus', 'haiku'].includes(frontmatter.model)) {
|
|
71
|
+
throw new Error('Invalid skill frontmatter: model must be sonnet, opus, or haiku');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (frontmatter['when-to-use'] && !Array.isArray(frontmatter['when-to-use'])) {
|
|
75
|
+
throw new Error('Invalid skill frontmatter: when-to-use must be an array');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (frontmatter.examples && !Array.isArray(frontmatter.examples)) {
|
|
79
|
+
throw new Error('Invalid skill frontmatter: examples must be an array');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (frontmatter.tags && !Array.isArray(frontmatter.tags)) {
|
|
83
|
+
throw new Error('Invalid skill frontmatter: tags must be an array');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (frontmatter.related && !Array.isArray(frontmatter.related)) {
|
|
87
|
+
throw new Error('Invalid skill frontmatter: related must be an array');
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cache Manager
|
|
3
|
+
* Centralized caching abstraction with TTL and size limits
|
|
4
|
+
*
|
|
5
|
+
* @module lib/utils/cache-manager
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Cache manager with TTL and size limits
|
|
12
|
+
*/
|
|
13
|
+
class CacheManager {
|
|
14
|
+
/**
|
|
15
|
+
* Create a new cache manager
|
|
16
|
+
* @param {Object} options - Cache configuration
|
|
17
|
+
* @param {number} options.maxSize - Maximum number of entries (default: 100)
|
|
18
|
+
* @param {number} options.ttl - Time-to-live in milliseconds (default: 60000)
|
|
19
|
+
* @param {number} options.maxValueSize - Maximum size per value in bytes (default: null - unlimited)
|
|
20
|
+
*/
|
|
21
|
+
constructor(options = {}) {
|
|
22
|
+
this.maxSize = options.maxSize || 100;
|
|
23
|
+
this.ttl = options.ttl || 60000; // 1 minute default
|
|
24
|
+
this.maxValueSize = options.maxValueSize || null;
|
|
25
|
+
|
|
26
|
+
// Use Map for insertion-order guarantee (FIFO eviction)
|
|
27
|
+
this._cache = new Map();
|
|
28
|
+
this._timestamps = new Map();
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Get a value from cache
|
|
33
|
+
* @param {string} key - Cache key
|
|
34
|
+
* @returns {*} Cached value or undefined if not found/expired
|
|
35
|
+
*/
|
|
36
|
+
get(key) {
|
|
37
|
+
if (!this._cache.has(key)) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Check if expired
|
|
42
|
+
const timestamp = this._timestamps.get(key);
|
|
43
|
+
if (Date.now() - timestamp > this.ttl) {
|
|
44
|
+
this.delete(key);
|
|
45
|
+
return undefined;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return this._cache.get(key);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Set a value in cache
|
|
53
|
+
* @param {string} key - Cache key
|
|
54
|
+
* @param {*} value - Value to cache
|
|
55
|
+
* @returns {boolean} True if cached, false if value too large
|
|
56
|
+
*/
|
|
57
|
+
set(key, value) {
|
|
58
|
+
// Check value size if limit set
|
|
59
|
+
if (this.maxValueSize !== null && typeof value === 'string') {
|
|
60
|
+
if (value.length > this.maxValueSize) {
|
|
61
|
+
return false; // Value too large, don't cache
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Update or add entry
|
|
66
|
+
this._cache.set(key, value);
|
|
67
|
+
this._timestamps.set(key, Date.now());
|
|
68
|
+
|
|
69
|
+
// Enforce size limit with FIFO eviction
|
|
70
|
+
this._enforceMaxSize();
|
|
71
|
+
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if key exists and is not expired
|
|
77
|
+
* @param {string} key - Cache key
|
|
78
|
+
* @returns {boolean} True if key exists and is valid
|
|
79
|
+
*/
|
|
80
|
+
has(key) {
|
|
81
|
+
return this.get(key) !== undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Delete a key from cache
|
|
86
|
+
* @param {string} key - Cache key
|
|
87
|
+
* @returns {boolean} True if key existed
|
|
88
|
+
*/
|
|
89
|
+
delete(key) {
|
|
90
|
+
this._timestamps.delete(key);
|
|
91
|
+
return this._cache.delete(key);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Clear all cache entries
|
|
96
|
+
*/
|
|
97
|
+
clear() {
|
|
98
|
+
this._cache.clear();
|
|
99
|
+
this._timestamps.clear();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get current cache size
|
|
104
|
+
* @returns {number} Number of entries
|
|
105
|
+
*/
|
|
106
|
+
get size() {
|
|
107
|
+
return this._cache.size;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Get cache statistics
|
|
112
|
+
* @returns {Object} Stats object with size, ttl, maxSize
|
|
113
|
+
*/
|
|
114
|
+
getStats() {
|
|
115
|
+
return {
|
|
116
|
+
size: this._cache.size,
|
|
117
|
+
maxSize: this.maxSize,
|
|
118
|
+
ttl: this.ttl,
|
|
119
|
+
maxValueSize: this.maxValueSize
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Enforce maximum cache size using FIFO eviction
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
_enforceMaxSize() {
|
|
128
|
+
// Map maintains insertion order - first key is oldest
|
|
129
|
+
while (this._cache.size > this.maxSize) {
|
|
130
|
+
const firstKey = this._cache.keys().next().value;
|
|
131
|
+
this.delete(firstKey);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Remove expired entries (useful for long-running processes)
|
|
137
|
+
* @returns {number} Number of entries removed
|
|
138
|
+
*/
|
|
139
|
+
prune() {
|
|
140
|
+
let removed = 0;
|
|
141
|
+
const now = Date.now();
|
|
142
|
+
|
|
143
|
+
for (const [key, timestamp] of this._timestamps.entries()) {
|
|
144
|
+
if (now - timestamp > this.ttl) {
|
|
145
|
+
this.delete(key);
|
|
146
|
+
removed++;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return removed;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
module.exports = { CacheManager };
|
|
@@ -8,42 +8,11 @@
|
|
|
8
8
|
* @license MIT
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
*/
|
|
17
|
-
function escapeShell(str) {
|
|
18
|
-
if (typeof str !== 'string') return '';
|
|
19
|
-
// Reject null bytes and newlines which could be used for injection
|
|
20
|
-
if (str.includes('\0') || str.includes('\n') || str.includes('\r')) {
|
|
21
|
-
throw new Error('Input contains invalid characters (null bytes or newlines)');
|
|
22
|
-
}
|
|
23
|
-
// Escape all shell metacharacters: " $ ` \ ! ; | & > < ( ) { } [ ] * ? ~ # ' space tab
|
|
24
|
-
return str.replace(/["\$`\\!;|&><(){}[\]*?~#'\s]/g, '\\$&');
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Escape single quotes for shell (replace ' with '\''
|
|
29
|
-
* @param {string} str - String to escape
|
|
30
|
-
* @returns {string} Escaped string safe for single-quoted shell use
|
|
31
|
-
*/
|
|
32
|
-
function escapeSingleQuotes(str) {
|
|
33
|
-
if (typeof str !== 'string') return '';
|
|
34
|
-
return str.replace(/'/g, "'\\''");
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Validate and sanitize file extension
|
|
39
|
-
* @param {string} ext - Extension to validate
|
|
40
|
-
* @returns {string} Safe extension (alphanumeric only)
|
|
41
|
-
*/
|
|
42
|
-
function sanitizeExtension(ext) {
|
|
43
|
-
if (typeof ext !== 'string') return 'ts';
|
|
44
|
-
const safe = ext.replace(/[^a-zA-Z0-9]/g, '');
|
|
45
|
-
return safe || 'ts';
|
|
46
|
-
}
|
|
11
|
+
const {
|
|
12
|
+
escapeShell,
|
|
13
|
+
escapeSingleQuotes,
|
|
14
|
+
sanitizeExtension
|
|
15
|
+
} = require('./shell-escape');
|
|
47
16
|
|
|
48
17
|
/**
|
|
49
18
|
* Validate git branch name to prevent command injection
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deprecation Warning Utility
|
|
3
|
+
* Centralized utility for handling deprecation warnings across the codebase
|
|
4
|
+
*
|
|
5
|
+
* @author Avi Fenesh
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
// Track which functions have already shown deprecation warnings (once per function)
|
|
10
|
+
const _deprecationWarned = new Set();
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Show deprecation warning for sync functions (once per function name)
|
|
14
|
+
* @param {string} funcName - Name of the deprecated sync function
|
|
15
|
+
* @param {string} asyncAlt - Name of the async alternative
|
|
16
|
+
*/
|
|
17
|
+
function warnDeprecation(funcName, asyncAlt) {
|
|
18
|
+
if (_deprecationWarned.has(funcName)) return;
|
|
19
|
+
_deprecationWarned.add(funcName);
|
|
20
|
+
console.warn(
|
|
21
|
+
`DEPRECATED: ${funcName}() is synchronous and blocks the event loop. ` +
|
|
22
|
+
`Use ${asyncAlt}() instead. Will be removed in v3.0.0.`
|
|
23
|
+
);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Reset deprecation warnings (for testing only)
|
|
28
|
+
* @private
|
|
29
|
+
*/
|
|
30
|
+
function _resetDeprecationWarnings() {
|
|
31
|
+
_deprecationWarned.clear();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
module.exports = {
|
|
35
|
+
warnDeprecation,
|
|
36
|
+
_resetDeprecationWarnings
|
|
37
|
+
};
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell Escaping Utilities
|
|
3
|
+
* Centralized string escaping functions for safe shell command construction
|
|
4
|
+
*
|
|
5
|
+
* @module lib/utils/shell-escape
|
|
6
|
+
* @author Avi Fenesh
|
|
7
|
+
* @license MIT
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Escape shell special characters for safe command interpolation
|
|
12
|
+
* Handles all dangerous shell metacharacters including command injection vectors
|
|
13
|
+
* Optimized: uses single regex test instead of multiple .includes() calls
|
|
14
|
+
* @param {string} str - String to escape
|
|
15
|
+
* @returns {string} Escaped string safe for shell use
|
|
16
|
+
* @throws {Error} If string contains null bytes or newlines
|
|
17
|
+
*/
|
|
18
|
+
function escapeShell(str) {
|
|
19
|
+
if (typeof str !== 'string') return '';
|
|
20
|
+
|
|
21
|
+
// Reject null bytes and newlines which could be used for injection
|
|
22
|
+
// Optimized: single regex test instead of 3 separate .includes() scans
|
|
23
|
+
// Use \x00 instead of \0 for better portability across JS engines
|
|
24
|
+
if (/[\x00\n\r]/.test(str)) {
|
|
25
|
+
throw new Error('Input contains invalid characters (null bytes or newlines)');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Escape all shell metacharacters: " $ ` \ ! ; | & > < ( ) { } [ ] * ? ~ # ' space tab
|
|
29
|
+
return str.replace(/["\$`\\!;|&><(){}[\]*?~#'\s]/g, '\\$&');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Escape single quotes for shell (replace ' with '\''
|
|
34
|
+
* Use this for strings that will be wrapped in single quotes
|
|
35
|
+
* @param {string} str - String to escape
|
|
36
|
+
* @returns {string} Escaped string safe for single-quoted shell use
|
|
37
|
+
*/
|
|
38
|
+
function escapeSingleQuotes(str) {
|
|
39
|
+
if (typeof str !== 'string') return '';
|
|
40
|
+
return str.replace(/'/g, "'\\''");
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Validate and sanitize file extension
|
|
45
|
+
* Removes all non-alphanumeric characters
|
|
46
|
+
* @param {string} ext - Extension to validate
|
|
47
|
+
* @returns {string} Safe extension (alphanumeric only), defaults to 'ts' if empty
|
|
48
|
+
*/
|
|
49
|
+
function sanitizeExtension(ext) {
|
|
50
|
+
if (typeof ext !== 'string') return 'ts';
|
|
51
|
+
const safe = ext.replace(/[^a-zA-Z0-9]/g, '');
|
|
52
|
+
return safe || 'ts';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Escape a string for use in a double-quoted shell context
|
|
57
|
+
* More permissive than escapeShell but still safe
|
|
58
|
+
* @param {string} str - String to escape
|
|
59
|
+
* @returns {string} Escaped string safe for double-quoted shell use
|
|
60
|
+
*/
|
|
61
|
+
function escapeDoubleQuotes(str) {
|
|
62
|
+
if (typeof str !== 'string') return '';
|
|
63
|
+
|
|
64
|
+
// In double quotes, we need to escape: $ ` " \ and newlines
|
|
65
|
+
return str.replace(/[$`"\\\n]/g, '\\$&');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Quote a string for safe shell use
|
|
70
|
+
* Wraps in single quotes and escapes any embedded single quotes
|
|
71
|
+
* This is often safer than escapeShell for complex strings
|
|
72
|
+
* @param {string} str - String to quote
|
|
73
|
+
* @returns {string} Safely quoted string
|
|
74
|
+
*/
|
|
75
|
+
function quoteShell(str) {
|
|
76
|
+
if (typeof str !== 'string') return "''";
|
|
77
|
+
|
|
78
|
+
// Wrap in single quotes and escape any embedded single quotes
|
|
79
|
+
return "'" + str.replace(/'/g, "'\\''") + "'";
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
escapeShell,
|
|
84
|
+
escapeSingleQuotes,
|
|
85
|
+
sanitizeExtension,
|
|
86
|
+
escapeDoubleQuotes,
|
|
87
|
+
quoteShell
|
|
88
|
+
};
|