@paths.design/caws-cli 3.5.0 โ 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/budget-derivation.d.ts +41 -2
- package/dist/budget-derivation.d.ts.map +1 -1
- package/dist/budget-derivation.js +417 -30
- package/dist/commands/archive.d.ts +50 -0
- package/dist/commands/archive.d.ts.map +1 -0
- package/dist/commands/archive.js +353 -0
- package/dist/commands/iterate.d.ts.map +1 -1
- package/dist/commands/iterate.js +12 -13
- package/dist/commands/mode.d.ts +24 -0
- package/dist/commands/mode.d.ts.map +1 -0
- package/dist/commands/mode.js +259 -0
- package/dist/commands/plan.d.ts +49 -0
- package/dist/commands/plan.d.ts.map +1 -0
- package/dist/commands/plan.js +448 -0
- package/dist/commands/quality-gates.d.ts +52 -0
- package/dist/commands/quality-gates.d.ts.map +1 -0
- package/dist/commands/quality-gates.js +490 -0
- package/dist/commands/specs.d.ts +71 -0
- package/dist/commands/specs.d.ts.map +1 -0
- package/dist/commands/specs.js +735 -0
- package/dist/commands/status.d.ts +4 -3
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +552 -22
- package/dist/commands/tutorial.d.ts +55 -0
- package/dist/commands/tutorial.d.ts.map +1 -0
- package/dist/commands/tutorial.js +481 -0
- package/dist/commands/validate.d.ts +10 -2
- package/dist/commands/validate.d.ts.map +1 -1
- package/dist/commands/validate.js +199 -39
- package/dist/config/modes.d.ts +225 -0
- package/dist/config/modes.d.ts.map +1 -0
- package/dist/config/modes.js +321 -0
- package/dist/constants/spec-types.d.ts +41 -0
- package/dist/constants/spec-types.d.ts.map +1 -0
- package/dist/constants/spec-types.js +42 -0
- package/dist/index-new.d.ts +5 -0
- package/dist/index-new.d.ts.map +1 -0
- package/dist/index-new.js +317 -0
- package/dist/index.js +227 -10
- package/dist/index.js.backup +4711 -0
- package/dist/policy/PolicyManager.d.ts +104 -0
- package/dist/policy/PolicyManager.d.ts.map +1 -0
- package/dist/policy/PolicyManager.js +399 -0
- package/dist/scaffold/cursor-hooks.d.ts.map +1 -1
- package/dist/scaffold/cursor-hooks.js +15 -0
- package/dist/scaffold/git-hooks.d.ts.map +1 -1
- package/dist/scaffold/git-hooks.js +32 -44
- package/dist/scaffold/index.d.ts.map +1 -1
- package/dist/scaffold/index.js +19 -0
- package/dist/spec/SpecFileManager.d.ts +146 -0
- package/dist/spec/SpecFileManager.d.ts.map +1 -0
- package/dist/spec/SpecFileManager.js +419 -0
- package/dist/utils/quality-gates-errors.js +520 -0
- package/dist/utils/quality-gates.d.ts +49 -0
- package/dist/utils/quality-gates.d.ts.map +1 -0
- package/dist/utils/quality-gates.js +361 -0
- package/dist/utils/spec-resolver.d.ts +88 -0
- package/dist/utils/spec-resolver.d.ts.map +1 -0
- package/dist/utils/spec-resolver.js +602 -0
- package/dist/validation/spec-validation.d.ts +14 -0
- package/dist/validation/spec-validation.d.ts.map +1 -1
- package/dist/validation/spec-validation.js +225 -13
- package/package.json +6 -5
- package/templates/.cursor/hooks/caws-scope-guard.sh +64 -8
- package/templates/.cursor/hooks/validate-spec.sh +22 -12
- package/templates/.cursor/rules/00-claims-verification.mdc +144 -0
- package/templates/.cursor/rules/01-working-style.mdc +50 -0
- package/templates/.cursor/rules/02-quality-gates.mdc +370 -0
- package/templates/.cursor/rules/03-naming-and-refactor.mdc +33 -0
- package/templates/.cursor/rules/04-logging-language-style.mdc +23 -0
- package/templates/.cursor/rules/05-safe-defaults-guards.mdc +23 -0
- package/templates/.cursor/rules/06-typescript-conventions.mdc +36 -0
- package/templates/.cursor/rules/07-process-ops.mdc +20 -0
- package/templates/.cursor/rules/08-solid-and-architecture.mdc +16 -0
- package/templates/.cursor/rules/09-docstrings.mdc +89 -0
- package/templates/.cursor/rules/10-authorship-and-attribution.mdc +15 -0
- package/templates/.cursor/rules/11-documentation-quality-standards.mdc +390 -0
- package/templates/.cursor/rules/12-scope-management-waivers.mdc +385 -0
- package/templates/.cursor/rules/13-implementation-completeness.mdc +516 -0
- package/templates/.cursor/rules/14-language-agnostic-standards.mdc +588 -0
- package/templates/.cursor/rules/15-sophisticated-todo-detection.mdc +425 -0
- package/templates/.cursor/rules/README.md +150 -0
- package/templates/apps/tools/caws/prompt-lint.js.backup +274 -0
- package/templates/apps/tools/caws/provenance.js.backup +73 -0
- package/templates/scripts/quality-gates/check-god-objects.js +146 -0
- package/templates/scripts/quality-gates/run-quality-gates.js +50 -0
- package/templates/scripts/v3/analysis/todo_analyzer.py +1950 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Policy Manager - Handles policy loading with intelligent caching
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - TTL-based caching for performance
|
|
6
|
+
* - Graceful fallback to defaults when policy.yaml missing
|
|
7
|
+
* - Cache inspection and management API
|
|
8
|
+
* - Waiver validation and delta application
|
|
9
|
+
*/
|
|
10
|
+
export class PolicyManager {
|
|
11
|
+
constructor(options?: {});
|
|
12
|
+
enableCaching: any;
|
|
13
|
+
cacheTTL: any;
|
|
14
|
+
policyCache: Map<any, any>;
|
|
15
|
+
/**
|
|
16
|
+
* Load CAWS policy from policy.yaml with caching
|
|
17
|
+
*
|
|
18
|
+
* @param {string} projectRoot - Project root directory
|
|
19
|
+
* @param {Object} options - Loading options
|
|
20
|
+
* @param {boolean} options.useCache - Use cache if available (default: true)
|
|
21
|
+
* @param {number} options.cacheTTL - Cache TTL override in milliseconds
|
|
22
|
+
* @returns {Promise<Object>} Policy object
|
|
23
|
+
*/
|
|
24
|
+
loadPolicy(projectRoot: string, options?: {
|
|
25
|
+
useCache: boolean;
|
|
26
|
+
cacheTTL: number;
|
|
27
|
+
}): Promise<any>;
|
|
28
|
+
/**
|
|
29
|
+
* Load a waiver document by ID
|
|
30
|
+
*
|
|
31
|
+
* @param {string} waiverId - Waiver ID (e.g., WV-0001)
|
|
32
|
+
* @param {string} projectRoot - Project root directory
|
|
33
|
+
* @returns {Promise<Object|null>} Waiver document or null if not found
|
|
34
|
+
*/
|
|
35
|
+
loadWaiver(waiverId: string, projectRoot: string): Promise<any | null>;
|
|
36
|
+
/**
|
|
37
|
+
* Check if a waiver is currently valid
|
|
38
|
+
*
|
|
39
|
+
* @param {Object} waiver - Waiver document
|
|
40
|
+
* @returns {boolean} True if waiver is valid and active
|
|
41
|
+
*/
|
|
42
|
+
isWaiverValid(waiver: any): boolean;
|
|
43
|
+
/**
|
|
44
|
+
* Apply waivers to baseline budget
|
|
45
|
+
*
|
|
46
|
+
* @param {Object} baseline - Baseline budget from policy
|
|
47
|
+
* @param {string[]} waiverIds - Array of waiver IDs to apply
|
|
48
|
+
* @param {string} projectRoot - Project root directory
|
|
49
|
+
* @returns {Promise<Object>} Effective budget with waivers applied
|
|
50
|
+
*/
|
|
51
|
+
applyWaivers(baseline: any, waiverIds: string[], projectRoot: string): Promise<any>;
|
|
52
|
+
/**
|
|
53
|
+
* Validate policy structure
|
|
54
|
+
*
|
|
55
|
+
* @param {Object} policy - Policy to validate
|
|
56
|
+
* @throws {Error} If policy is invalid
|
|
57
|
+
*/
|
|
58
|
+
validatePolicy(policy: any): void;
|
|
59
|
+
/**
|
|
60
|
+
* Get default CAWS policy
|
|
61
|
+
*
|
|
62
|
+
* Returns sensible defaults when policy.yaml doesn't exist.
|
|
63
|
+
*
|
|
64
|
+
* @returns {Object} Default policy configuration
|
|
65
|
+
*/
|
|
66
|
+
getDefaultPolicy(): any;
|
|
67
|
+
/**
|
|
68
|
+
* Clear policy cache
|
|
69
|
+
*
|
|
70
|
+
* @param {string} [projectRoot] - Specific project to clear, or all if omitted
|
|
71
|
+
*/
|
|
72
|
+
clearCache(projectRoot?: string): void;
|
|
73
|
+
/**
|
|
74
|
+
* Get cache status for a project
|
|
75
|
+
*
|
|
76
|
+
* @param {string} projectRoot - Project root directory
|
|
77
|
+
* @returns {Object} Cache status information
|
|
78
|
+
*/
|
|
79
|
+
getCacheStatus(projectRoot: string): any;
|
|
80
|
+
/**
|
|
81
|
+
* Reload policy from disk (bypassing cache)
|
|
82
|
+
*
|
|
83
|
+
* @param {string} projectRoot - Project root directory
|
|
84
|
+
* @returns {Promise<Object>} Fresh policy
|
|
85
|
+
*/
|
|
86
|
+
reloadPolicy(projectRoot: string): Promise<any>;
|
|
87
|
+
/**
|
|
88
|
+
* Get all cached projects
|
|
89
|
+
*
|
|
90
|
+
* @returns {string[]} Array of project roots with cached policies
|
|
91
|
+
*/
|
|
92
|
+
getCachedProjects(): string[];
|
|
93
|
+
/**
|
|
94
|
+
* Get cache statistics
|
|
95
|
+
*
|
|
96
|
+
* @returns {Object} Cache statistics
|
|
97
|
+
*/
|
|
98
|
+
getCacheStats(): any;
|
|
99
|
+
}
|
|
100
|
+
export const defaultPolicyManager: PolicyManager;
|
|
101
|
+
export declare function loadPolicy(projectRoot: any, options: any): Promise<any>;
|
|
102
|
+
export declare function clearCache(projectRoot: any): void;
|
|
103
|
+
export declare function getCacheStatus(projectRoot: any): any;
|
|
104
|
+
//# sourceMappingURL=PolicyManager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PolicyManager.d.ts","sourceRoot":"","sources":["../../src/policy/PolicyManager.js"],"names":[],"mappings":"AAWA;;;;;;;;GAQG;AACH;IACE,0BAIC;IAHC,mBAAkD;IAClD,cAA0C;IAC1C,2BAA4B;IAG9B;;;;;;;;OAQG;IACH,wBANW,MAAM,YAEd;QAAyB,QAAQ,EAAzB,OAAO;QACS,QAAQ,EAAxB,MAAM;KACd,GAAU,OAAO,KAAQ,CAuE3B;IAED;;;;;;OAMG;IACH,qBAJW,MAAM,eACN,MAAM,GACJ,OAAO,CAAC,MAAO,IAAI,CAAC,CAchC;IAED;;;;;OAKG;IACH,4BAFa,OAAO,CA2BnB;IAED;;;;;;;OAOG;IACH,uCAJW,MAAM,EAAE,eACR,MAAM,GACJ,OAAO,KAAQ,CA2B3B;IAED;;;;;OAKG;IACH,kCA8BC;IAED;;;;;;OAMG;IACH,wBAoDC;IAED;;;;OAIG;IACH,yBAFW,MAAM,QAQhB;IAED;;;;;OAKG;IACH,4BAHW,MAAM,OAmBhB;IAED;;;;;OAKG;IACH,0BAHW,MAAM,GACJ,OAAO,KAAQ,CAK3B;IAED;;;;OAIG;IACH,qBAFa,MAAM,EAAE,CAIpB;IAED;;;;OAIG;IACH,qBA2BC;CACF;AAGD,iDAAiD;AAOnC,iFAA+E;AAC/E,2DAA6D;AACzD,8DAAiE"}
|
|
@@ -0,0 +1,399 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Policy Manager with Intelligent Caching
|
|
3
|
+
* Manages policy.yaml loading with caching, default fallback, and waiver validation.
|
|
4
|
+
* Ported from agent-agency v2 CAWS integration patterns.
|
|
5
|
+
* @author @darianrosebrook
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const fs = require('fs-extra');
|
|
9
|
+
const path = require('path');
|
|
10
|
+
const yaml = require('js-yaml');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Policy Manager - Handles policy loading with intelligent caching
|
|
14
|
+
*
|
|
15
|
+
* Features:
|
|
16
|
+
* - TTL-based caching for performance
|
|
17
|
+
* - Graceful fallback to defaults when policy.yaml missing
|
|
18
|
+
* - Cache inspection and management API
|
|
19
|
+
* - Waiver validation and delta application
|
|
20
|
+
*/
|
|
21
|
+
class PolicyManager {
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.enableCaching = options.enableCaching ?? true;
|
|
24
|
+
this.cacheTTL = options.cacheTTL ?? 300000; // 5 minutes default
|
|
25
|
+
this.policyCache = new Map(); // projectRoot -> { policy, cachedAt, ttl }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Load CAWS policy from policy.yaml with caching
|
|
30
|
+
*
|
|
31
|
+
* @param {string} projectRoot - Project root directory
|
|
32
|
+
* @param {Object} options - Loading options
|
|
33
|
+
* @param {boolean} options.useCache - Use cache if available (default: true)
|
|
34
|
+
* @param {number} options.cacheTTL - Cache TTL override in milliseconds
|
|
35
|
+
* @returns {Promise<Object>} Policy object
|
|
36
|
+
*/
|
|
37
|
+
async loadPolicy(projectRoot, options = {}) {
|
|
38
|
+
const useCache = options.useCache ?? this.enableCaching;
|
|
39
|
+
const cacheTTL = options.cacheTTL ?? this.cacheTTL;
|
|
40
|
+
const startTime = Date.now();
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Check cache first
|
|
44
|
+
if (useCache && this.policyCache.has(projectRoot)) {
|
|
45
|
+
const cached = this.policyCache.get(projectRoot);
|
|
46
|
+
const cacheAge = Date.now() - cached.cachedAt;
|
|
47
|
+
|
|
48
|
+
if (cacheAge < cacheTTL) {
|
|
49
|
+
return {
|
|
50
|
+
...cached.policy,
|
|
51
|
+
_cacheHit: true,
|
|
52
|
+
_loadDuration: Date.now() - startTime,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Load from file
|
|
58
|
+
const policyPath = path.join(projectRoot, '.caws', 'policy.yaml');
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const content = await fs.readFile(policyPath, 'utf-8');
|
|
62
|
+
const policy = yaml.load(content);
|
|
63
|
+
|
|
64
|
+
// Validate policy structure
|
|
65
|
+
this.validatePolicy(policy);
|
|
66
|
+
|
|
67
|
+
// Update cache
|
|
68
|
+
if (this.enableCaching) {
|
|
69
|
+
this.policyCache.set(projectRoot, {
|
|
70
|
+
policy,
|
|
71
|
+
cachedAt: Date.now(),
|
|
72
|
+
ttl: cacheTTL,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return {
|
|
77
|
+
...policy,
|
|
78
|
+
_cacheHit: false,
|
|
79
|
+
_loadDuration: Date.now() - startTime,
|
|
80
|
+
};
|
|
81
|
+
} catch (error) {
|
|
82
|
+
if (error.code === 'ENOENT') {
|
|
83
|
+
// Policy file doesn't exist - use default
|
|
84
|
+
const defaultPolicy = this.getDefaultPolicy();
|
|
85
|
+
|
|
86
|
+
if (this.enableCaching) {
|
|
87
|
+
this.policyCache.set(projectRoot, {
|
|
88
|
+
policy: defaultPolicy,
|
|
89
|
+
cachedAt: Date.now(),
|
|
90
|
+
ttl: cacheTTL,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return {
|
|
95
|
+
...defaultPolicy,
|
|
96
|
+
_isDefault: true,
|
|
97
|
+
_cacheHit: false,
|
|
98
|
+
_loadDuration: Date.now() - startTime,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
throw error;
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
throw new Error(`Policy load failed: ${error.message}`);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Load a waiver document by ID
|
|
110
|
+
*
|
|
111
|
+
* @param {string} waiverId - Waiver ID (e.g., WV-0001)
|
|
112
|
+
* @param {string} projectRoot - Project root directory
|
|
113
|
+
* @returns {Promise<Object|null>} Waiver document or null if not found
|
|
114
|
+
*/
|
|
115
|
+
async loadWaiver(waiverId, projectRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const waiverPath = path.join(projectRoot, '.caws', 'waivers', `${waiverId}.yaml`);
|
|
118
|
+
|
|
119
|
+
const content = await fs.readFile(waiverPath, 'utf-8');
|
|
120
|
+
return yaml.load(content);
|
|
121
|
+
} catch (error) {
|
|
122
|
+
if (error.code === 'ENOENT') {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
throw new Error(`Failed to load waiver ${waiverId}: ${error.message}`);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Check if a waiver is currently valid
|
|
131
|
+
*
|
|
132
|
+
* @param {Object} waiver - Waiver document
|
|
133
|
+
* @returns {boolean} True if waiver is valid and active
|
|
134
|
+
*/
|
|
135
|
+
isWaiverValid(waiver) {
|
|
136
|
+
if (!waiver) {
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Check status
|
|
141
|
+
if (waiver.status !== 'active') {
|
|
142
|
+
return false;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Check expiry
|
|
146
|
+
if (waiver.expires_at) {
|
|
147
|
+
const expiryDate = new Date(waiver.expires_at);
|
|
148
|
+
const now = new Date();
|
|
149
|
+
if (now > expiryDate) {
|
|
150
|
+
return false;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check if it has required approvals
|
|
155
|
+
if (!waiver.approvers || waiver.approvers.length === 0) {
|
|
156
|
+
return false;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Apply waivers to baseline budget
|
|
164
|
+
*
|
|
165
|
+
* @param {Object} baseline - Baseline budget from policy
|
|
166
|
+
* @param {string[]} waiverIds - Array of waiver IDs to apply
|
|
167
|
+
* @param {string} projectRoot - Project root directory
|
|
168
|
+
* @returns {Promise<Object>} Effective budget with waivers applied
|
|
169
|
+
*/
|
|
170
|
+
async applyWaivers(baseline, waiverIds, projectRoot) {
|
|
171
|
+
const effective = { ...baseline };
|
|
172
|
+
const applied = [];
|
|
173
|
+
|
|
174
|
+
for (const waiverId of waiverIds) {
|
|
175
|
+
const waiver = await this.loadWaiver(waiverId, projectRoot);
|
|
176
|
+
|
|
177
|
+
if (waiver && this.isWaiverValid(waiver)) {
|
|
178
|
+
// Apply additive delta
|
|
179
|
+
if (waiver.delta) {
|
|
180
|
+
if (waiver.delta.max_files) {
|
|
181
|
+
effective.max_files += waiver.delta.max_files;
|
|
182
|
+
}
|
|
183
|
+
if (waiver.delta.max_loc) {
|
|
184
|
+
effective.max_loc += waiver.delta.max_loc;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
applied.push(waiverId);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return {
|
|
192
|
+
effective,
|
|
193
|
+
applied,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Validate policy structure
|
|
199
|
+
*
|
|
200
|
+
* @param {Object} policy - Policy to validate
|
|
201
|
+
* @throws {Error} If policy is invalid
|
|
202
|
+
*/
|
|
203
|
+
validatePolicy(policy) {
|
|
204
|
+
if (!policy.version) {
|
|
205
|
+
throw new Error('Policy missing version field');
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (!policy.risk_tiers) {
|
|
209
|
+
throw new Error('Policy missing risk_tiers configuration');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Validate all tiers have required fields
|
|
213
|
+
for (const tier of [1, 2, 3]) {
|
|
214
|
+
const budget = policy.risk_tiers[tier];
|
|
215
|
+
if (!budget) {
|
|
216
|
+
throw new Error(`Policy missing risk tier ${tier} configuration`);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (typeof budget.max_files !== 'number' || typeof budget.max_loc !== 'number') {
|
|
220
|
+
throw new Error(`Risk tier ${tier} missing or invalid budget limits`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Validate edit rules if present
|
|
225
|
+
if (policy.edit_rules) {
|
|
226
|
+
if (typeof policy.edit_rules.policy_and_code_same_pr !== 'boolean') {
|
|
227
|
+
throw new Error('edit_rules.policy_and_code_same_pr must be boolean');
|
|
228
|
+
}
|
|
229
|
+
if (typeof policy.edit_rules.min_approvers_for_budget_raise !== 'number') {
|
|
230
|
+
throw new Error('edit_rules.min_approvers_for_budget_raise must be number');
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Get default CAWS policy
|
|
237
|
+
*
|
|
238
|
+
* Returns sensible defaults when policy.yaml doesn't exist.
|
|
239
|
+
*
|
|
240
|
+
* @returns {Object} Default policy configuration
|
|
241
|
+
*/
|
|
242
|
+
getDefaultPolicy() {
|
|
243
|
+
return {
|
|
244
|
+
version: 1,
|
|
245
|
+
risk_tiers: {
|
|
246
|
+
1: {
|
|
247
|
+
max_files: 25,
|
|
248
|
+
max_loc: 1000,
|
|
249
|
+
description: 'Critical changes requiring manual review',
|
|
250
|
+
},
|
|
251
|
+
2: {
|
|
252
|
+
max_files: 50,
|
|
253
|
+
max_loc: 2000,
|
|
254
|
+
description: 'Standard features with automated gates',
|
|
255
|
+
},
|
|
256
|
+
3: {
|
|
257
|
+
max_files: 100,
|
|
258
|
+
max_loc: 5000,
|
|
259
|
+
description: 'Low-risk changes with minimal oversight',
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
edit_rules: {
|
|
263
|
+
policy_and_code_same_pr: false,
|
|
264
|
+
min_approvers_for_budget_raise: 2,
|
|
265
|
+
require_signed_commits: true,
|
|
266
|
+
},
|
|
267
|
+
gates: {
|
|
268
|
+
budget_limit: {
|
|
269
|
+
enabled: true,
|
|
270
|
+
description: 'Enforce change budget limits',
|
|
271
|
+
},
|
|
272
|
+
spec_completeness: {
|
|
273
|
+
enabled: true,
|
|
274
|
+
description: 'Require complete working specifications',
|
|
275
|
+
},
|
|
276
|
+
contract_compliance: {
|
|
277
|
+
enabled: true,
|
|
278
|
+
description: 'Validate API contracts',
|
|
279
|
+
},
|
|
280
|
+
coverage_threshold: {
|
|
281
|
+
enabled: true,
|
|
282
|
+
description: 'Maintain test coverage requirements',
|
|
283
|
+
},
|
|
284
|
+
mutation_threshold: {
|
|
285
|
+
enabled: true,
|
|
286
|
+
description: 'Require mutation testing for T1/T2 changes',
|
|
287
|
+
},
|
|
288
|
+
security_scan: {
|
|
289
|
+
enabled: true,
|
|
290
|
+
description: 'Run security vulnerability scans',
|
|
291
|
+
},
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Clear policy cache
|
|
298
|
+
*
|
|
299
|
+
* @param {string} [projectRoot] - Specific project to clear, or all if omitted
|
|
300
|
+
*/
|
|
301
|
+
clearCache(projectRoot) {
|
|
302
|
+
if (projectRoot) {
|
|
303
|
+
this.policyCache.delete(projectRoot);
|
|
304
|
+
} else {
|
|
305
|
+
this.policyCache.clear();
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
/**
|
|
310
|
+
* Get cache status for a project
|
|
311
|
+
*
|
|
312
|
+
* @param {string} projectRoot - Project root directory
|
|
313
|
+
* @returns {Object} Cache status information
|
|
314
|
+
*/
|
|
315
|
+
getCacheStatus(projectRoot) {
|
|
316
|
+
const cached = this.policyCache.get(projectRoot);
|
|
317
|
+
|
|
318
|
+
if (!cached) {
|
|
319
|
+
return {
|
|
320
|
+
cached: false,
|
|
321
|
+
ttl: this.cacheTTL,
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
return {
|
|
326
|
+
cached: true,
|
|
327
|
+
age: Date.now() - cached.cachedAt,
|
|
328
|
+
ttl: cached.ttl,
|
|
329
|
+
remainingTTL: Math.max(0, cached.ttl - (Date.now() - cached.cachedAt)),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Reload policy from disk (bypassing cache)
|
|
335
|
+
*
|
|
336
|
+
* @param {string} projectRoot - Project root directory
|
|
337
|
+
* @returns {Promise<Object>} Fresh policy
|
|
338
|
+
*/
|
|
339
|
+
async reloadPolicy(projectRoot) {
|
|
340
|
+
this.clearCache(projectRoot);
|
|
341
|
+
return this.loadPolicy(projectRoot, { useCache: false });
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Get all cached projects
|
|
346
|
+
*
|
|
347
|
+
* @returns {string[]} Array of project roots with cached policies
|
|
348
|
+
*/
|
|
349
|
+
getCachedProjects() {
|
|
350
|
+
return Array.from(this.policyCache.keys());
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Get cache statistics
|
|
355
|
+
*
|
|
356
|
+
* @returns {Object} Cache statistics
|
|
357
|
+
*/
|
|
358
|
+
getCacheStats() {
|
|
359
|
+
const projects = this.getCachedProjects();
|
|
360
|
+
const now = Date.now();
|
|
361
|
+
|
|
362
|
+
const stats = {
|
|
363
|
+
totalCached: projects.length,
|
|
364
|
+
validCaches: 0,
|
|
365
|
+
expiredCaches: 0,
|
|
366
|
+
totalAge: 0,
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
for (const project of projects) {
|
|
370
|
+
const cached = this.policyCache.get(project);
|
|
371
|
+
const age = now - cached.cachedAt;
|
|
372
|
+
|
|
373
|
+
stats.totalAge += age;
|
|
374
|
+
|
|
375
|
+
if (age < cached.ttl) {
|
|
376
|
+
stats.validCaches++;
|
|
377
|
+
} else {
|
|
378
|
+
stats.expiredCaches++;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
stats.averageAge = projects.length > 0 ? stats.totalAge / projects.length : 0;
|
|
383
|
+
|
|
384
|
+
return stats;
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Export singleton instance with default configuration
|
|
389
|
+
const defaultPolicyManager = new PolicyManager();
|
|
390
|
+
|
|
391
|
+
module.exports = {
|
|
392
|
+
PolicyManager,
|
|
393
|
+
defaultPolicyManager,
|
|
394
|
+
|
|
395
|
+
// Convenience exports for backward compatibility
|
|
396
|
+
loadPolicy: (projectRoot, options) => defaultPolicyManager.loadPolicy(projectRoot, options),
|
|
397
|
+
clearCache: (projectRoot) => defaultPolicyManager.clearCache(projectRoot),
|
|
398
|
+
getCacheStatus: (projectRoot) => defaultPolicyManager.getCacheStatus(projectRoot),
|
|
399
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor-hooks.d.ts","sourceRoot":"","sources":["../../src/scaffold/cursor-hooks.js"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,gDAHW,MAAM,WACN,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"cursor-hooks.d.ts","sourceRoot":"","sources":["../../src/scaffold/cursor-hooks.js"],"names":[],"mappings":"AAaA;;;;GAIG;AACH,gDAHW,MAAM,WACN,MAAM,EAAE,iBAqJlB"}
|
|
@@ -138,6 +138,21 @@ async function scaffoldCursorHooks(projectDir, levels = ['safety', 'quality', 's
|
|
|
138
138
|
await fs.copy(readmePath, path.join(cursorDir, 'README.md'));
|
|
139
139
|
}
|
|
140
140
|
|
|
141
|
+
// Copy rules directory if it exists
|
|
142
|
+
const rulesTemplateDir = path.join(cursorTemplateDir, 'rules');
|
|
143
|
+
const rulesDestDir = path.join(cursorDir, 'rules');
|
|
144
|
+
if (fs.existsSync(rulesTemplateDir)) {
|
|
145
|
+
try {
|
|
146
|
+
await fs.ensureDir(rulesDestDir);
|
|
147
|
+
await fs.copy(rulesTemplateDir, rulesDestDir);
|
|
148
|
+
const ruleFiles = fs.readdirSync(rulesTemplateDir).filter((file) => file.endsWith('.mdc'));
|
|
149
|
+
console.log(chalk.green('โ
Cursor rules configured'));
|
|
150
|
+
console.log(chalk.gray(` Rules: ${ruleFiles.length} rule files installed`));
|
|
151
|
+
} catch (error) {
|
|
152
|
+
console.warn(chalk.yellow('โ ๏ธ Failed to copy Cursor rules:'), error.message);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
141
156
|
console.log(chalk.green('โ
Cursor hooks configured'));
|
|
142
157
|
console.log(chalk.gray(` Enabled: ${levels.join(', ')}`));
|
|
143
158
|
console.log(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"git-hooks.d.ts","sourceRoot":"","sources":["../../src/scaffold/git-hooks.js"],"names":[],"mappings":"AASA;;;;GAIG;AACH,6CAHW,MAAM;;;GAwGhB;
|
|
1
|
+
{"version":3,"file":"git-hooks.d.ts","sourceRoot":"","sources":["../../src/scaffold/git-hooks.js"],"names":[],"mappings":"AASA;;;;GAIG;AACH,6CAHW,MAAM;;;GAwGhB;AAwND;;;GAGG;AACH,2CAFW,MAAM,iBAkChB;AAED;;;GAGG;AACH,gDAFW,MAAM,iBAgDhB"}
|
|
@@ -116,10 +116,10 @@ async function scaffoldGitHooks(projectDir, options = {}) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
/**
|
|
119
|
-
* Generate pre-commit hook content
|
|
119
|
+
* Generate pre-commit hook content with staged file quality gates
|
|
120
120
|
*/
|
|
121
121
|
function generatePreCommitHook(options) {
|
|
122
|
-
const { qualityGates = true } = options;
|
|
122
|
+
const { qualityGates = true, stagedOnly = true } = options;
|
|
123
123
|
|
|
124
124
|
return `#!/bin/bash
|
|
125
125
|
# CAWS Pre-commit Hook
|
|
@@ -127,8 +127,8 @@ function generatePreCommitHook(options) {
|
|
|
127
127
|
|
|
128
128
|
set -e
|
|
129
129
|
|
|
130
|
-
echo "
|
|
131
|
-
echo "
|
|
130
|
+
echo "๐ฆ Running CAWS Quality Gates${qualityGates ? ' (Crisis Response Mode)' : ''}..."
|
|
131
|
+
echo "๐ Analyzing ${stagedOnly ? 'staged files only' : 'all files'}..."
|
|
132
132
|
|
|
133
133
|
# Check if CAWS is initialized
|
|
134
134
|
if [ ! -d ".caws" ]; then
|
|
@@ -136,55 +136,43 @@ if [ ! -d ".caws" ]; then
|
|
|
136
136
|
exit 0
|
|
137
137
|
fi
|
|
138
138
|
|
|
139
|
-
# Run
|
|
140
|
-
if command -v
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
echo "โ
CAWS validation passed"
|
|
139
|
+
# Run quality gates
|
|
140
|
+
if command -v node >/dev/null 2>&1; then
|
|
141
|
+
if node scripts/quality-gates/run-quality-gates.js; then
|
|
142
|
+
echo "โ
Quality gates passed"
|
|
144
143
|
else
|
|
145
|
-
echo "โ
|
|
146
|
-
echo "๐ก Fix
|
|
144
|
+
echo "โ Quality gates failed - commit blocked"
|
|
145
|
+
echo "๐ก Fix the violations above before committing"
|
|
146
|
+
echo "๐ See docs/refactoring.md for crisis response plan"
|
|
147
147
|
exit 1
|
|
148
148
|
fi
|
|
149
149
|
else
|
|
150
|
-
echo "โ ๏ธ
|
|
150
|
+
echo "โ ๏ธ Node.js not found - skipping quality gates"
|
|
151
|
+
echo "๐ก Install Node.js to enable automatic quality checking"
|
|
152
|
+
exit 0
|
|
151
153
|
fi
|
|
152
154
|
|
|
153
|
-
# Run
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
echo "
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
echo "
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
echo "๐ก Fix issues or skip with: git commit --no-verify (allowed)"
|
|
167
|
-
exit 1
|
|
168
|
-
fi
|
|
169
|
-
fi
|
|
170
|
-
|
|
171
|
-
# Run tests if available
|
|
172
|
-
if [ -f "package.json" ] && grep -q '"test"' package.json; then
|
|
173
|
-
echo "๐งช Running tests..."
|
|
174
|
-
if npm test; then
|
|
175
|
-
echo "โ
Tests passed"
|
|
176
|
-
else
|
|
177
|
-
echo "โ Tests failed"
|
|
178
|
-
echo "๐ก Fix issues or skip with: git commit --no-verify (allowed)"
|
|
179
|
-
exit 1
|
|
180
|
-
fi
|
|
155
|
+
# Run hidden TODO analysis on staged files only
|
|
156
|
+
echo "๐ Checking for hidden TODOs in staged files..."
|
|
157
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
158
|
+
if python3 scripts/v3/analysis/todo_analyzer.py --staged-only --ci-mode --min-confidence 0.8 >/dev/null 2>&1; then
|
|
159
|
+
echo "โ
No critical hidden TODOs found in staged files"
|
|
160
|
+
else
|
|
161
|
+
echo "โ Critical hidden TODOs detected in staged files - commit blocked"
|
|
162
|
+
echo "๐ก Fix stub implementations and placeholder code before committing"
|
|
163
|
+
echo "๐ See docs/PLACEHOLDER-DETECTION-GUIDE.md for classification"
|
|
164
|
+
echo ""
|
|
165
|
+
echo "๐ Running detailed analysis on staged files..."
|
|
166
|
+
python3 scripts/v3/analysis/todo_analyzer.py --staged-only --min-confidence 0.8
|
|
167
|
+
exit 1
|
|
181
168
|
fi
|
|
169
|
+
else
|
|
170
|
+
echo "โ ๏ธ Python3 not found - skipping hidden TODO analysis"
|
|
171
|
+
echo "๐ก Install Python3 to enable automatic TODO checking"
|
|
182
172
|
fi
|
|
183
|
-
`
|
|
184
|
-
: ''
|
|
185
|
-
}
|
|
186
173
|
|
|
187
|
-
echo "
|
|
174
|
+
echo "โ
All quality checks passed - proceeding with commit"
|
|
175
|
+
exit 0
|
|
188
176
|
`;
|
|
189
177
|
}
|
|
190
178
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.js"],"names":[],"mappings":"AAkKA;;;GAGG;AACH,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/scaffold/index.js"],"names":[],"mappings":"AAkKA;;;GAGG;AACH,6DA6VC;AAhfD;;;;GAIG;AACH,mDAHW,MAAM;;;GA8HhB;AAMD;;;GAGG;AACH,yDAGC"}
|
package/dist/scaffold/index.js
CHANGED
|
@@ -311,6 +311,25 @@ async function scaffoldProject(options) {
|
|
|
311
311
|
},
|
|
312
312
|
});
|
|
313
313
|
|
|
314
|
+
// Add quality gates scripts for staged file analysis
|
|
315
|
+
enhancements.push({
|
|
316
|
+
name: 'scripts/quality-gates/run-quality-gates.js',
|
|
317
|
+
description: 'Quality gates runner for staged files',
|
|
318
|
+
required: false,
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
enhancements.push({
|
|
322
|
+
name: 'scripts/quality-gates/check-god-objects.js',
|
|
323
|
+
description: 'God object detector for staged files',
|
|
324
|
+
required: false,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
enhancements.push({
|
|
328
|
+
name: 'scripts/v3/analysis/todo_analyzer.py',
|
|
329
|
+
description: 'Advanced hidden TODO analyzer with dependency resolution',
|
|
330
|
+
required: false,
|
|
331
|
+
});
|
|
332
|
+
|
|
314
333
|
// Add commit conventions for setups that don't have them
|
|
315
334
|
if (!setup.hasTemplates || !fs.existsSync(path.join(currentDir, 'COMMIT_CONVENTIONS.md'))) {
|
|
316
335
|
enhancements.push({
|