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.
Files changed (151) hide show
  1. package/.claude-plugin/marketplace.json +6 -6
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/CHANGELOG.md +123 -1
  4. package/README.md +186 -159
  5. package/SECURITY.md +25 -81
  6. package/adapters/codex/install.sh +58 -16
  7. package/adapters/opencode/install.sh +92 -23
  8. package/lib/index.js +47 -4
  9. package/lib/patterns/review-patterns.js +58 -11
  10. package/lib/patterns/slop-patterns.js +154 -147
  11. package/lib/platform/detect-platform.js +99 -350
  12. package/lib/platform/detection-configs.js +93 -0
  13. package/lib/platform/state-dir.js +122 -0
  14. package/lib/platform/verify-tools.js +10 -78
  15. package/lib/schemas/README.md +195 -0
  16. package/lib/schemas/validator.js +247 -0
  17. package/lib/sources/custom-handler.js +199 -0
  18. package/lib/sources/policy-questions.js +239 -0
  19. package/lib/sources/source-cache.js +164 -0
  20. package/lib/state/workflow-state.js +368 -665
  21. package/lib/types/README.md +292 -0
  22. package/lib/types/agent-frontmatter.d.ts +134 -0
  23. package/lib/types/command-frontmatter.d.ts +107 -0
  24. package/lib/types/hook-frontmatter.d.ts +115 -0
  25. package/lib/types/index.d.ts +84 -0
  26. package/lib/types/plugin-manifest.d.ts +102 -0
  27. package/lib/types/skill-frontmatter.d.ts +89 -0
  28. package/lib/utils/cache-manager.js +154 -0
  29. package/lib/utils/context-optimizer.js +5 -36
  30. package/lib/utils/deprecation.js +37 -0
  31. package/lib/utils/shell-escape.js +88 -0
  32. package/mcp-server/index.js +513 -22
  33. package/package.json +6 -2
  34. package/plugins/deslop-around/.claude-plugin/plugin.json +1 -1
  35. package/plugins/deslop-around/lib/index.js +170 -0
  36. package/plugins/deslop-around/lib/patterns/review-patterns.js +58 -11
  37. package/plugins/deslop-around/lib/patterns/slop-patterns.js +169 -129
  38. package/plugins/deslop-around/lib/platform/detect-platform.js +162 -316
  39. package/plugins/deslop-around/lib/platform/detection-configs.js +93 -0
  40. package/plugins/deslop-around/lib/platform/state-dir.js +122 -0
  41. package/plugins/deslop-around/lib/platform/verify-tools.js +10 -78
  42. package/plugins/deslop-around/lib/schemas/README.md +195 -0
  43. package/plugins/deslop-around/lib/schemas/validator.js +247 -0
  44. package/plugins/deslop-around/lib/sources/custom-handler.js +199 -0
  45. package/plugins/deslop-around/lib/sources/policy-questions.js +239 -0
  46. package/plugins/deslop-around/lib/sources/source-cache.js +164 -0
  47. package/plugins/deslop-around/lib/state/workflow-state.js +387 -484
  48. package/plugins/deslop-around/lib/types/README.md +292 -0
  49. package/plugins/deslop-around/lib/types/agent-frontmatter.d.ts +134 -0
  50. package/plugins/deslop-around/lib/types/command-frontmatter.d.ts +107 -0
  51. package/plugins/deslop-around/lib/types/hook-frontmatter.d.ts +115 -0
  52. package/plugins/deslop-around/lib/types/index.d.ts +84 -0
  53. package/plugins/deslop-around/lib/types/plugin-manifest.d.ts +102 -0
  54. package/plugins/deslop-around/lib/types/skill-frontmatter.d.ts +89 -0
  55. package/plugins/deslop-around/lib/utils/cache-manager.js +154 -0
  56. package/plugins/deslop-around/lib/utils/context-optimizer.js +115 -37
  57. package/plugins/deslop-around/lib/utils/deprecation.js +37 -0
  58. package/plugins/deslop-around/lib/utils/shell-escape.js +88 -0
  59. package/plugins/next-task/.claude-plugin/plugin.json +1 -1
  60. package/plugins/next-task/agents/delivery-validator.md +2 -2
  61. package/plugins/next-task/agents/implementation-agent.md +3 -4
  62. package/plugins/next-task/agents/planning-agent.md +77 -19
  63. package/plugins/next-task/agents/review-orchestrator.md +21 -122
  64. package/plugins/next-task/agents/task-discoverer.md +164 -23
  65. package/plugins/next-task/commands/next-task.md +180 -14
  66. package/plugins/next-task/lib/index.js +170 -0
  67. package/plugins/next-task/lib/patterns/review-patterns.js +58 -11
  68. package/plugins/next-task/lib/patterns/slop-patterns.js +169 -129
  69. package/plugins/next-task/lib/platform/detect-platform.js +162 -316
  70. package/plugins/next-task/lib/platform/detection-configs.js +93 -0
  71. package/plugins/next-task/lib/platform/state-dir.js +122 -0
  72. package/plugins/next-task/lib/platform/verify-tools.js +10 -78
  73. package/plugins/next-task/lib/schemas/README.md +195 -0
  74. package/plugins/next-task/lib/schemas/validator.js +247 -0
  75. package/plugins/next-task/lib/sources/custom-handler.js +199 -0
  76. package/plugins/next-task/lib/sources/policy-questions.js +239 -0
  77. package/plugins/next-task/lib/sources/source-cache.js +164 -0
  78. package/plugins/next-task/lib/state/workflow-state.js +387 -484
  79. package/plugins/next-task/lib/types/README.md +292 -0
  80. package/plugins/next-task/lib/types/agent-frontmatter.d.ts +134 -0
  81. package/plugins/next-task/lib/types/command-frontmatter.d.ts +107 -0
  82. package/plugins/next-task/lib/types/hook-frontmatter.d.ts +115 -0
  83. package/plugins/next-task/lib/types/index.d.ts +84 -0
  84. package/plugins/next-task/lib/types/plugin-manifest.d.ts +102 -0
  85. package/plugins/next-task/lib/types/skill-frontmatter.d.ts +89 -0
  86. package/plugins/next-task/lib/utils/cache-manager.js +154 -0
  87. package/plugins/next-task/lib/utils/context-optimizer.js +115 -37
  88. package/plugins/next-task/lib/utils/deprecation.js +37 -0
  89. package/plugins/next-task/lib/utils/shell-escape.js +88 -0
  90. package/plugins/project-review/.claude-plugin/plugin.json +1 -1
  91. package/plugins/project-review/lib/index.js +170 -0
  92. package/plugins/project-review/lib/patterns/review-patterns.js +58 -11
  93. package/plugins/project-review/lib/patterns/slop-patterns.js +169 -129
  94. package/plugins/project-review/lib/platform/detect-platform.js +162 -316
  95. package/plugins/project-review/lib/platform/detection-configs.js +93 -0
  96. package/plugins/project-review/lib/platform/state-dir.js +122 -0
  97. package/plugins/project-review/lib/platform/verify-tools.js +10 -78
  98. package/plugins/project-review/lib/schemas/README.md +195 -0
  99. package/plugins/project-review/lib/schemas/validator.js +247 -0
  100. package/plugins/project-review/lib/sources/custom-handler.js +199 -0
  101. package/plugins/project-review/lib/sources/policy-questions.js +239 -0
  102. package/plugins/project-review/lib/sources/source-cache.js +164 -0
  103. package/plugins/project-review/lib/state/workflow-state.js +387 -484
  104. package/plugins/project-review/lib/types/README.md +292 -0
  105. package/plugins/project-review/lib/types/agent-frontmatter.d.ts +134 -0
  106. package/plugins/project-review/lib/types/command-frontmatter.d.ts +107 -0
  107. package/plugins/project-review/lib/types/hook-frontmatter.d.ts +115 -0
  108. package/plugins/project-review/lib/types/index.d.ts +84 -0
  109. package/plugins/project-review/lib/types/plugin-manifest.d.ts +102 -0
  110. package/plugins/project-review/lib/types/skill-frontmatter.d.ts +89 -0
  111. package/plugins/project-review/lib/utils/cache-manager.js +154 -0
  112. package/plugins/project-review/lib/utils/context-optimizer.js +115 -37
  113. package/plugins/project-review/lib/utils/deprecation.js +37 -0
  114. package/plugins/project-review/lib/utils/shell-escape.js +88 -0
  115. package/plugins/reality-check/.claude-plugin/plugin.json +1 -1
  116. package/plugins/reality-check/agents/code-explorer.md +1 -1
  117. package/plugins/ship/.claude-plugin/plugin.json +1 -1
  118. package/plugins/ship/lib/index.js +170 -0
  119. package/plugins/ship/lib/patterns/review-patterns.js +58 -11
  120. package/plugins/ship/lib/patterns/slop-patterns.js +169 -129
  121. package/plugins/ship/lib/platform/detect-platform.js +162 -316
  122. package/plugins/ship/lib/platform/detection-configs.js +93 -0
  123. package/plugins/ship/lib/platform/state-dir.js +122 -0
  124. package/plugins/ship/lib/platform/verify-tools.js +10 -78
  125. package/plugins/ship/lib/schemas/README.md +195 -0
  126. package/plugins/ship/lib/schemas/validator.js +247 -0
  127. package/plugins/ship/lib/sources/custom-handler.js +199 -0
  128. package/plugins/ship/lib/sources/policy-questions.js +239 -0
  129. package/plugins/ship/lib/sources/source-cache.js +164 -0
  130. package/plugins/ship/lib/state/workflow-state.js +387 -484
  131. package/plugins/ship/lib/types/README.md +292 -0
  132. package/plugins/ship/lib/types/agent-frontmatter.d.ts +134 -0
  133. package/plugins/ship/lib/types/command-frontmatter.d.ts +107 -0
  134. package/plugins/ship/lib/types/hook-frontmatter.d.ts +115 -0
  135. package/plugins/ship/lib/types/index.d.ts +84 -0
  136. package/plugins/ship/lib/types/plugin-manifest.d.ts +102 -0
  137. package/plugins/ship/lib/types/skill-frontmatter.d.ts +89 -0
  138. package/plugins/ship/lib/utils/cache-manager.js +154 -0
  139. package/plugins/ship/lib/utils/context-optimizer.js +115 -37
  140. package/plugins/ship/lib/utils/deprecation.js +37 -0
  141. package/plugins/ship/lib/utils/shell-escape.js +88 -0
  142. package/scripts/install/codex.sh +216 -72
  143. package/scripts/install/opencode.sh +197 -21
  144. package/lib/state/workflow-state.schema.json +0 -282
  145. package/plugins/deslop-around/lib/state/workflow-state.schema.json +0 -282
  146. package/plugins/next-task/agents/policy-selector.md +0 -248
  147. package/plugins/next-task/lib/state/tasks-registry.schema.json +0 -85
  148. package/plugins/next-task/lib/state/workflow-state.schema.json +0 -282
  149. package/plugins/next-task/lib/state/worktree-status.schema.json +0 -219
  150. package/plugins/project-review/lib/state/workflow-state.schema.json +0 -282
  151. 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
- * Escape shell special characters for safe command interpolation
13
- * Handles all dangerous shell metacharacters including command injection vectors
14
- * @param {string} str - String to escape
15
- * @returns {string} Escaped string safe for shell use
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
+ };