@paths.design/caws-cli 3.0.0 → 3.1.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 (95) hide show
  1. package/README.md +295 -150
  2. package/dist/budget-derivation.d.ts +35 -0
  3. package/dist/budget-derivation.d.ts.map +1 -0
  4. package/dist/budget-derivation.js +204 -0
  5. package/dist/cicd-optimizer.d.ts +142 -0
  6. package/dist/cicd-optimizer.d.ts.map +1 -0
  7. package/dist/cicd-optimizer.js +504 -0
  8. package/dist/commands/burnup.d.ts +6 -0
  9. package/dist/commands/burnup.d.ts.map +1 -0
  10. package/dist/commands/burnup.js +90 -0
  11. package/dist/commands/init.d.ts +5 -0
  12. package/dist/commands/init.d.ts.map +1 -0
  13. package/dist/commands/init.js +514 -0
  14. package/dist/commands/provenance.d.ts +22 -0
  15. package/dist/commands/provenance.d.ts.map +1 -0
  16. package/dist/commands/provenance.js +594 -0
  17. package/dist/commands/tool.d.ts +13 -0
  18. package/dist/commands/tool.d.ts.map +1 -0
  19. package/dist/commands/tool.js +138 -0
  20. package/dist/commands/validate.d.ts +7 -0
  21. package/dist/commands/validate.d.ts.map +1 -0
  22. package/dist/commands/validate.js +80 -0
  23. package/dist/config/index.d.ts +29 -0
  24. package/dist/config/index.d.ts.map +1 -0
  25. package/dist/config/index.js +132 -0
  26. package/dist/error-handler.d.ts +50 -0
  27. package/dist/error-handler.d.ts.map +1 -0
  28. package/dist/error-handler.js +253 -0
  29. package/dist/generators/working-spec.d.ts +13 -0
  30. package/dist/generators/working-spec.d.ts.map +1 -0
  31. package/dist/generators/working-spec.js +204 -0
  32. package/dist/index-new.d.ts +5 -0
  33. package/dist/index-new.d.ts.map +1 -0
  34. package/dist/index-new.js +317 -0
  35. package/dist/index.d.ts +3 -12
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +100 -1659
  38. package/dist/index.js.backup +4711 -0
  39. package/dist/scaffold/cursor-hooks.d.ts +7 -0
  40. package/dist/scaffold/cursor-hooks.d.ts.map +1 -0
  41. package/dist/scaffold/cursor-hooks.js +152 -0
  42. package/dist/scaffold/index.d.ts +20 -0
  43. package/dist/scaffold/index.d.ts.map +1 -0
  44. package/dist/scaffold/index.js +486 -0
  45. package/dist/test-analysis.d.ts +182 -0
  46. package/dist/test-analysis.d.ts.map +1 -0
  47. package/dist/test-analysis.js +580 -0
  48. package/dist/tool-interface.d.ts +236 -0
  49. package/dist/tool-interface.d.ts.map +1 -0
  50. package/dist/tool-interface.js +314 -0
  51. package/dist/tool-loader.d.ts +77 -0
  52. package/dist/tool-loader.d.ts.map +1 -0
  53. package/dist/tool-loader.js +298 -0
  54. package/dist/tool-validator.d.ts +72 -0
  55. package/dist/tool-validator.d.ts.map +1 -0
  56. package/dist/tool-validator.js +387 -0
  57. package/dist/utils/detection.d.ts +7 -0
  58. package/dist/utils/detection.d.ts.map +1 -0
  59. package/dist/utils/detection.js +174 -0
  60. package/dist/utils/finalization.d.ts +17 -0
  61. package/dist/utils/finalization.d.ts.map +1 -0
  62. package/dist/utils/finalization.js +229 -0
  63. package/dist/utils/project-analysis.d.ts +14 -0
  64. package/dist/utils/project-analysis.d.ts.map +1 -0
  65. package/dist/utils/project-analysis.js +105 -0
  66. package/dist/validation/spec-validation.d.ts +29 -0
  67. package/dist/validation/spec-validation.d.ts.map +1 -0
  68. package/dist/validation/spec-validation.js +376 -0
  69. package/dist/waivers-manager.d.ts +167 -0
  70. package/dist/waivers-manager.d.ts.map +1 -0
  71. package/dist/waivers-manager.js +549 -0
  72. package/package.json +10 -12
  73. package/templates/.cursor/README.md +311 -0
  74. package/templates/.cursor/hooks/audit.sh +55 -0
  75. package/templates/.cursor/hooks/block-dangerous.sh +77 -0
  76. package/templates/.cursor/hooks/caws-quality-check.sh +52 -0
  77. package/templates/.cursor/hooks/caws-scope-guard.sh +74 -0
  78. package/templates/.cursor/hooks/caws-tool-validation.sh +121 -0
  79. package/templates/.cursor/hooks/format.sh +38 -0
  80. package/templates/.cursor/hooks/naming-check.sh +64 -0
  81. package/templates/.cursor/hooks/scan-secrets.sh +46 -0
  82. package/templates/.cursor/hooks/scope-guard.sh +52 -0
  83. package/templates/.cursor/hooks/validate-spec.sh +38 -0
  84. package/templates/.cursor/hooks.json +59 -0
  85. package/templates/.github/copilot/instructions.md +311 -0
  86. package/templates/.idea/runConfigurations/CAWS_Evaluate.xml +5 -0
  87. package/templates/.idea/runConfigurations/CAWS_Validate.xml +5 -0
  88. package/templates/.vscode/launch.json +56 -0
  89. package/templates/.vscode/settings.json +93 -0
  90. package/templates/.windsurf/workflows/caws-guided-development.md +92 -0
  91. package/templates/apps/tools/caws/README.md +1 -1
  92. package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
  93. package/templates/apps/tools/caws/provenance.js.backup +73 -0
  94. package/templates/apps/tools/caws/schemas/working-spec.schema.json +21 -3
  95. package/templates/codemod/test.js +93 -1
@@ -0,0 +1,298 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * @fileoverview CAWS Tool Loader - Dynamic tool discovery and loading system
5
+ * Provides secure, sandboxed loading of tools from apps/tools/caws/ directory
6
+ * @author @darianrosebrook
7
+ */
8
+
9
+ const fs = require('fs');
10
+ const path = require('path');
11
+ const { EventEmitter } = require('events');
12
+ const { setTimeout, clearTimeout } = require('timers');
13
+ const { safeAsync } = require('./error-handler');
14
+
15
+ /**
16
+ * Tool Loader - Discovers, validates, and loads CAWS tools dynamically
17
+ * @extends EventEmitter
18
+ */
19
+ class ToolLoader extends EventEmitter {
20
+ constructor(options = {}) {
21
+ super();
22
+ this.options = {
23
+ toolsDir: options.toolsDir || path.join(process.cwd(), 'apps/tools/caws'),
24
+ cacheEnabled: options.cacheEnabled !== false,
25
+ timeout: options.timeout || 10000,
26
+ maxTools: options.maxTools || 50,
27
+ ...options,
28
+ };
29
+
30
+ this.loadedTools = new Map();
31
+ this.discoveredTools = new Set();
32
+ this.loadingState = 'idle'; // idle, discovering, loading, ready, error
33
+ }
34
+
35
+ /**
36
+ * Discover available tools in the tools directory
37
+ * @returns {Promise<Array<string>>} Array of tool file paths
38
+ */
39
+ async discoverTools() {
40
+ return safeAsync(async () => {
41
+ this.loadingState = 'discovering';
42
+ this.emit('discovery:start');
43
+
44
+ // Check if tools directory exists
45
+ if (!fs.existsSync(this.options.toolsDir)) {
46
+ this.emit('discovery:complete', { tools: [], reason: 'directory_not_found' });
47
+ this.loadingState = 'ready';
48
+ return [];
49
+ }
50
+
51
+ // Read directory contents
52
+ const files = await fs.promises.readdir(this.options.toolsDir);
53
+
54
+ // Filter for valid tool files
55
+ const toolFiles = files
56
+ .filter((file) => {
57
+ // Must be .js file
58
+ if (!file.endsWith('.js')) return false;
59
+
60
+ // Must not be hidden or backup file
61
+ if (file.startsWith('.') || file.includes('.backup')) return false;
62
+
63
+ // Must not be test file (unless explicitly allowed)
64
+ if (file.includes('.test.') && !this.options.includeTests) return false;
65
+
66
+ return true;
67
+ })
68
+ .map((file) => path.join(this.options.toolsDir, file))
69
+ .filter((filePath) => {
70
+ // Validate file exists and is readable
71
+ try {
72
+ const stats = fs.statSync(filePath);
73
+ return stats.isFile() && stats.size > 0 && stats.size < 1024 * 1024; // < 1MB
74
+ } catch (error) {
75
+ this.emit('discovery:warning', { file: filePath, error: error.message });
76
+ return false;
77
+ }
78
+ })
79
+ .slice(0, this.options.maxTools); // Limit number of tools
80
+
81
+ this.discoveredTools = new Set(toolFiles);
82
+ this.emit('discovery:complete', { tools: toolFiles, count: toolFiles.length });
83
+ this.loadingState = 'idle';
84
+
85
+ return toolFiles;
86
+ }, 'Tool discovery failed');
87
+ }
88
+
89
+ /**
90
+ * Load a specific tool module
91
+ * @param {string} toolPath - Path to tool file
92
+ * @returns {Promise<Object>} Loaded tool module
93
+ */
94
+ async loadTool(toolPath) {
95
+ return safeAsync(async () => {
96
+ const toolId = path.basename(toolPath, '.js');
97
+
98
+ // Check cache first
99
+ if (this.loadedTools.has(toolId) && this.options.cacheEnabled) {
100
+ return this.loadedTools.get(toolId);
101
+ }
102
+
103
+ this.emit('tool:loading', { id: toolId, path: toolPath });
104
+
105
+ // Validate tool file before loading
106
+ await this.validateToolFile(toolPath);
107
+
108
+ // Load the module with timeout
109
+ const toolModule = await this.loadModuleWithTimeout(toolPath);
110
+
111
+ // Validate tool interface
112
+ await this.validateToolInterface(toolModule, toolId);
113
+
114
+ // Cache the loaded tool
115
+ const tool = {
116
+ module: toolModule,
117
+ path: toolPath,
118
+ loadedAt: new Date(),
119
+ metadata: toolModule.getMetadata ? toolModule.getMetadata() : {},
120
+ };
121
+
122
+ this.loadedTools.set(toolId, tool);
123
+ this.emit('tool:loaded', { id: toolId, metadata: tool.metadata });
124
+
125
+ return tool;
126
+ }, `Tool loading failed: ${toolId}`);
127
+ }
128
+
129
+ /**
130
+ * Load all discovered tools
131
+ * @returns {Promise<Map<string, Object>>} Map of loaded tools
132
+ */
133
+ async loadAllTools() {
134
+ this.loadingState = 'loading';
135
+ this.emit('loading:start');
136
+
137
+ const toolPaths = await this.discoverTools();
138
+ const results = new Map();
139
+
140
+ for (const toolPath of toolPaths) {
141
+ try {
142
+ const tool = await this.loadTool(toolPath);
143
+ results.set(path.basename(toolPath, '.js'), tool);
144
+ } catch (error) {
145
+ // Log error but continue loading other tools
146
+ this.emit('loading:warning', { path: toolPath, error: error.message });
147
+ }
148
+ }
149
+
150
+ this.loadingState = 'ready';
151
+ this.emit('loading:complete', { loaded: results.size, total: toolPaths.length });
152
+
153
+ return results;
154
+ }
155
+
156
+ /**
157
+ * Get a loaded tool by ID
158
+ * @param {string} toolId - Tool identifier
159
+ * @returns {Object|null} Tool object or null if not found
160
+ */
161
+ getTool(toolId) {
162
+ return this.loadedTools.get(toolId) || null;
163
+ }
164
+
165
+ /**
166
+ * Get all loaded tools
167
+ * @returns {Map<string, Object>} Map of loaded tools
168
+ */
169
+ getAllTools() {
170
+ return new Map(this.loadedTools);
171
+ }
172
+
173
+ /**
174
+ * Unload a tool (remove from cache)
175
+ * @param {string} toolId - Tool identifier
176
+ * @returns {boolean} True if tool was unloaded
177
+ */
178
+ unloadTool(toolId) {
179
+ const unloaded = this.loadedTools.delete(toolId);
180
+ if (unloaded) {
181
+ this.emit('tool:unloaded', { id: toolId });
182
+ }
183
+ return unloaded;
184
+ }
185
+
186
+ /**
187
+ * Validate tool file before loading
188
+ * @private
189
+ * @param {string} toolPath - Path to tool file
190
+ */
191
+ async validateToolFile(toolPath) {
192
+ // Basic file validation
193
+ const stats = await fs.promises.stat(toolPath);
194
+ if (stats.size === 0) {
195
+ throw new Error('Tool file is empty');
196
+ }
197
+
198
+ if (stats.size > 1024 * 1024) {
199
+ // 1MB limit
200
+ throw new Error('Tool file too large (>1MB)');
201
+ }
202
+
203
+ // Read first few lines to check for shebang and basic structure
204
+ const fd = await fs.promises.open(toolPath, 'r');
205
+ try {
206
+ const buffer = Buffer.alloc(512);
207
+ const { bytesRead } = await fd.read(buffer, 0, 512, 0);
208
+ const content = buffer.toString('utf8', 0, bytesRead);
209
+
210
+ // Check for shebang
211
+ if (!content.startsWith('#!/usr/bin/env node') && !content.startsWith('#!')) {
212
+ throw new Error('Tool file missing shebang');
213
+ }
214
+
215
+ // Basic syntax check - look for module.exports or ES modules
216
+ const hasExports = content.includes('module.exports') || content.includes('export ');
217
+ if (!hasExports) {
218
+ throw new Error('Tool file does not export anything');
219
+ }
220
+ } finally {
221
+ await fd.close();
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Load module with timeout protection
227
+ * @private
228
+ * @param {string} toolPath - Path to tool file
229
+ */
230
+ async loadModuleWithTimeout(toolPath) {
231
+ return new Promise((resolve, reject) => {
232
+ const timeout = setTimeout(() => {
233
+ reject(new Error(`Tool loading timeout after ${this.options.timeout}ms`));
234
+ }, this.options.timeout);
235
+
236
+ try {
237
+ // Clear require cache to ensure fresh load
238
+ delete require.cache[require.resolve(toolPath)];
239
+
240
+ const module = require(toolPath);
241
+ clearTimeout(timeout);
242
+ resolve(module);
243
+ } catch (error) {
244
+ clearTimeout(timeout);
245
+ reject(error);
246
+ }
247
+ });
248
+ }
249
+
250
+ /**
251
+ * Validate tool interface compliance
252
+ * @private
253
+ * @param {Object} toolModule - Loaded tool module
254
+ * @param {string} toolId - Tool identifier
255
+ */
256
+ async validateToolInterface(toolModule, toolId) {
257
+ const requiredMethods = ['execute', 'getMetadata'];
258
+
259
+ for (const method of requiredMethods) {
260
+ if (typeof toolModule[method] !== 'function') {
261
+ throw new Error(`Tool ${toolId} missing required method: ${method}`);
262
+ }
263
+ }
264
+
265
+ // Validate metadata structure
266
+ if (toolModule.getMetadata) {
267
+ const metadata = toolModule.getMetadata();
268
+ const requiredFields = ['id', 'name', 'version'];
269
+
270
+ for (const field of requiredFields) {
271
+ if (!metadata[field]) {
272
+ throw new Error(`Tool ${toolId} metadata missing required field: ${field}`);
273
+ }
274
+ }
275
+
276
+ // Validate metadata types
277
+ if (typeof metadata.id !== 'string' || typeof metadata.name !== 'string') {
278
+ throw new Error(`Tool ${toolId} metadata has invalid types`);
279
+ }
280
+ }
281
+ }
282
+
283
+ /**
284
+ * Get loader statistics
285
+ * @returns {Object} Statistics object
286
+ */
287
+ getStats() {
288
+ return {
289
+ discovered: this.discoveredTools.size,
290
+ loaded: this.loadedTools.size,
291
+ state: this.loadingState,
292
+ cacheEnabled: this.options.cacheEnabled,
293
+ toolsDir: this.options.toolsDir,
294
+ };
295
+ }
296
+ }
297
+
298
+ module.exports = ToolLoader;
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ export = ToolValidator;
3
+ /**
4
+ * Tool Validator - Security validation and allowlist enforcement
5
+ */
6
+ declare class ToolValidator {
7
+ constructor(options?: {});
8
+ options: {
9
+ allowlistPath: any;
10
+ strictMode: boolean;
11
+ maxFileSize: any;
12
+ };
13
+ allowlist: any;
14
+ validationCache: Map<any, any>;
15
+ /**
16
+ * Load and parse the tools allowlist
17
+ * @returns {Promise<Array<string>>} Array of allowed commands/patterns
18
+ */
19
+ loadAllowlist(): Promise<Array<string>>;
20
+ /**
21
+ * Validate a tool against security requirements
22
+ * @param {Object} tool - Tool object with module and metadata
23
+ * @returns {Promise<Object>} Validation result
24
+ */
25
+ validateTool(tool: any): Promise<any>;
26
+ /**
27
+ * Check file-level security
28
+ * @private
29
+ * @param {Object} tool - Tool object
30
+ */
31
+ private checkFileSecurity;
32
+ /**
33
+ * Check code-level security
34
+ * @private
35
+ * @param {Object} tool - Tool object
36
+ */
37
+ private checkCodeSecurity;
38
+ /**
39
+ * Check interface compliance
40
+ * @private
41
+ * @param {Object} tool - Tool object
42
+ */
43
+ private checkInterfaceCompliance;
44
+ /**
45
+ * Check metadata validity
46
+ * @private
47
+ * @param {Object} tool - Tool object
48
+ */
49
+ private checkMetadataValidity;
50
+ /**
51
+ * Check dependency safety
52
+ * @private
53
+ * @param {Object} tool - Tool object
54
+ */
55
+ private checkDependencySafety;
56
+ /**
57
+ * Validate a command against the allowlist
58
+ * @param {string} command - Command to validate
59
+ * @returns {boolean} True if command is allowed
60
+ */
61
+ validateCommand(command: string): boolean;
62
+ /**
63
+ * Clear validation cache
64
+ */
65
+ clearCache(): void;
66
+ /**
67
+ * Get validator statistics
68
+ * @returns {Object} Statistics object
69
+ */
70
+ getStats(): any;
71
+ }
72
+ //# sourceMappingURL=tool-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tool-validator.d.ts","sourceRoot":"","sources":["../src/tool-validator.js"],"names":[],"mappings":";;AAYA;;GAEG;AACH;IACE,0BAWC;IAVC;;;;MAMC;IAED,eAAqB;IACrB,+BAAgC;IAGlC;;;OAGG;IACH,iBAFa,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,CAqBlC;IAED;;;;OAIG;IACH,yBAFa,OAAO,KAAQ,CAsF3B;IAED;;;;OAIG;IACH,0BAgCC;IAED;;;;OAIG;IACH,0BA8CC;IAED;;;;OAIG;IACH,iCAkBC;IAED;;;;OAIG;IACH,8BAqCC;IAED;;;;OAIG;IACH,8BA4BC;IAED;;;;OAIG;IACH,yBAHW,MAAM,GACJ,OAAO,CAmBnB;IAED;;OAEG;IACH,mBAEC;IAED;;;OAGG;IACH,gBAOC;CACF"}