@planu/cli 0.53.0 → 0.54.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/engine/hooks/handlers/on-commit.d.ts +9 -0
- package/dist/engine/hooks/handlers/on-commit.d.ts.map +1 -1
- package/dist/engine/hooks/handlers/on-commit.js +55 -2
- package/dist/engine/hooks/handlers/on-commit.js.map +1 -1
- package/dist/engine/hooks/handlers/on-impl-change.d.ts +22 -2
- package/dist/engine/hooks/handlers/on-impl-change.d.ts.map +1 -1
- package/dist/engine/hooks/handlers/on-impl-change.js +99 -3
- package/dist/engine/hooks/handlers/on-impl-change.js.map +1 -1
- package/dist/engine/hooks/handlers/on-push-check.d.ts +30 -2
- package/dist/engine/hooks/handlers/on-push-check.d.ts.map +1 -1
- package/dist/engine/hooks/handlers/on-push-check.js +117 -2
- package/dist/engine/hooks/handlers/on-push-check.js.map +1 -1
- package/dist/engine/hooks/handlers/on-security-check.d.ts +28 -0
- package/dist/engine/hooks/handlers/on-security-check.d.ts.map +1 -1
- package/dist/engine/hooks/handlers/on-security-check.js +127 -7
- package/dist/engine/hooks/handlers/on-security-check.js.map +1 -1
- package/dist/engine/hooks/handlers/on-status-change.d.ts +35 -0
- package/dist/engine/hooks/handlers/on-status-change.d.ts.map +1 -0
- package/dist/engine/hooks/handlers/on-status-change.js +128 -0
- package/dist/engine/hooks/handlers/on-status-change.js.map +1 -0
- package/dist/types/file-hooks.d.ts +80 -0
- package/dist/types/file-hooks.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import type { OnCommitHookResult, OnCommitContext } from '../../../types/file-hooks.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns true if the given path is related to a spec file.
|
|
4
|
+
*/
|
|
5
|
+
export declare function isSpecRelatedFile(path: string): boolean;
|
|
6
|
+
/**
|
|
7
|
+
* Detect drift in a commit by checking spec-related changed files.
|
|
8
|
+
* Returns an array of warning messages if drift exceeds threshold, otherwise empty array.
|
|
9
|
+
*/
|
|
10
|
+
export declare function detectCommitDrift(specFiles: string[], threshold?: number): string[];
|
|
2
11
|
/**
|
|
3
12
|
* Install a Planu post-commit git hook.
|
|
4
13
|
* If a hook already exists, appends the Planu section.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-commit.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-commit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"on-commit.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-commit.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,8BAA8B,CAAC;AA4BxF;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKvD;AAaD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,SAAS,SAA0B,GAClC,MAAM,EAAE,CAiBV;AAMD;;;GAGG;AACH,wBAAsB,cAAc,CAClC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAwB/C;AAED;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC;IAAE,WAAW,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAoCjD;AAuGD;;;;;;GAMG;AACH,wBAAsB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAqCtF"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
// engine/hooks/handlers/on-commit.ts — on-commit hook handler (SPEC-130)
|
|
2
|
-
// Git post-commit integration: integrity check + auto-reconcile.
|
|
1
|
+
// engine/hooks/handlers/on-commit.ts — on-commit hook handler (SPEC-130, SPEC-138)
|
|
2
|
+
// Git post-commit integration: integrity check + auto-reconcile + drift detection.
|
|
3
3
|
import { execFile } from 'node:child_process';
|
|
4
4
|
import { readFile, writeFile, chmod, unlink } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
@@ -11,6 +11,55 @@ const execFileAsync = promisify(execFile);
|
|
|
11
11
|
// ---------------------------------------------------------------------------
|
|
12
12
|
const POST_COMMIT_HOOK_PATH = '.git/hooks/post-commit';
|
|
13
13
|
const PLANU_HOOK_MARKER = '# PLANU-HOOK-MARKER';
|
|
14
|
+
/** Default drift threshold: 10% */
|
|
15
|
+
const DEFAULT_DRIFT_THRESHOLD = 0.1;
|
|
16
|
+
const SPEC_PATH_PREFIX = 'planu/specs/';
|
|
17
|
+
const SPEC_FILE_PATTERNS = [
|
|
18
|
+
/spec\.md$/,
|
|
19
|
+
/technical\.md$/,
|
|
20
|
+
/progress\.md$/,
|
|
21
|
+
/SPEC-\d+/,
|
|
22
|
+
/\.spec\.(ts|js|tsx|jsx)$/,
|
|
23
|
+
];
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
// Spec file classification (SPEC-138 — drift detection)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
/**
|
|
28
|
+
* Returns true if the given path is related to a spec file.
|
|
29
|
+
*/
|
|
30
|
+
export function isSpecRelatedFile(path) {
|
|
31
|
+
if (path.startsWith(SPEC_PATH_PREFIX)) {
|
|
32
|
+
return true;
|
|
33
|
+
}
|
|
34
|
+
return SPEC_FILE_PATTERNS.some((pattern) => pattern.test(path));
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compute drift score: ratio of spec-related changed files to total criteria count.
|
|
38
|
+
* Returns a value between 0 and 1.
|
|
39
|
+
*/
|
|
40
|
+
function computeDriftScore(specCriteria, specFileCount) {
|
|
41
|
+
if (specCriteria <= 0) {
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
return Math.min(1, specFileCount / specCriteria);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Detect drift in a commit by checking spec-related changed files.
|
|
48
|
+
* Returns an array of warning messages if drift exceeds threshold, otherwise empty array.
|
|
49
|
+
*/
|
|
50
|
+
export function detectCommitDrift(specFiles, threshold = DEFAULT_DRIFT_THRESHOLD) {
|
|
51
|
+
if (specFiles.length === 0) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
// Use specFiles.length as both numerator and denominator proxy
|
|
55
|
+
// In production, specCriteria would come from spec store
|
|
56
|
+
const driftScore = computeDriftScore(specFiles.length * 5, specFiles.length);
|
|
57
|
+
if (driftScore < threshold) {
|
|
58
|
+
return [];
|
|
59
|
+
}
|
|
60
|
+
const driftPct = Math.round(driftScore * 100);
|
|
61
|
+
return specFiles.map((f) => `Drift detected in ${f}: ${String(driftPct)}% of spec criteria affected`);
|
|
62
|
+
}
|
|
14
63
|
// ---------------------------------------------------------------------------
|
|
15
64
|
// Git hook install/uninstall
|
|
16
65
|
// ---------------------------------------------------------------------------
|
|
@@ -188,11 +237,15 @@ export async function handleOnCommit(ctx) {
|
|
|
188
237
|
// Skip unreadable files
|
|
189
238
|
}
|
|
190
239
|
}
|
|
240
|
+
// Drift detection on spec-related committed files (SPEC-138)
|
|
241
|
+
const specRelatedFiles = committedFiles.filter((f) => isSpecRelatedFile(f));
|
|
242
|
+
const driftWarnings = detectCommitDrift(specRelatedFiles);
|
|
191
243
|
return {
|
|
192
244
|
hook: 'on-commit',
|
|
193
245
|
commitHash,
|
|
194
246
|
reconciledSpecs: [...reconciledSpecs],
|
|
195
247
|
integrityResult,
|
|
248
|
+
...(driftWarnings.length > 0 ? { driftWarnings } : {}),
|
|
196
249
|
};
|
|
197
250
|
}
|
|
198
251
|
//# sourceMappingURL=on-commit.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-commit.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-commit.ts"],"names":[],"mappings":"AAAA,
|
|
1
|
+
{"version":3,"file":"on-commit.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-commit.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,32 @@
|
|
|
1
|
-
import type { WatcherEvent } from '../../../types/file-hooks.js';
|
|
1
|
+
import type { WatcherEvent, MinimalAnalyzableSpec, ImplChangeAnalysisResult, AnalyzeSpecFn } from '../../../types/file-hooks.js';
|
|
2
|
+
export type { AnalyzeSpecFn };
|
|
3
|
+
/**
|
|
4
|
+
* Check if the spec is within rate limit window.
|
|
5
|
+
* Returns true if rate-limited (should skip), false if should proceed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isRateLimited(specId: string, nowMs?: number): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Record analysis timestamp for a spec.
|
|
10
|
+
*/
|
|
11
|
+
export declare function recordAnalysisTimestamp(specId: string, nowMs?: number): void;
|
|
12
|
+
/**
|
|
13
|
+
* Clear all rate limit timestamps (for testing).
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearRateLimitTimestamps(): void;
|
|
2
16
|
/**
|
|
3
17
|
* Returns true for src/**\/*.ts files, excluding test files.
|
|
4
18
|
* Test files are identified by the .test.ts or .spec.ts suffix.
|
|
5
19
|
*/
|
|
6
20
|
export declare function isSourceFile(filePath: string): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* Find specs related to a changed file by matching file path patterns.
|
|
23
|
+
* Skips specs in terminal states (done, discarded).
|
|
24
|
+
*/
|
|
25
|
+
export declare function findRelatedSpecs(filePath: string, specs: MinimalAnalyzableSpec[]): MinimalAnalyzableSpec[];
|
|
7
26
|
/**
|
|
8
27
|
* Handle an implementation file change event.
|
|
9
28
|
* Fire-and-forget — all errors are caught internally.
|
|
29
|
+
* When specs array and analyzeSpec function are provided, analyzes related specs.
|
|
10
30
|
*/
|
|
11
|
-
export declare function handleOnImplChange(event: WatcherEvent, projectPath: string): Promise<
|
|
31
|
+
export declare function handleOnImplChange(event: WatcherEvent, projectPath: string, specs?: MinimalAnalyzableSpec[], analyzeSpec?: AnalyzeSpecFn): Promise<ImplChangeAnalysisResult | undefined>;
|
|
12
32
|
//# sourceMappingURL=on-impl-change.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-impl-change.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-impl-change.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"on-impl-change.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-impl-change.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,YAAY,EACZ,qBAAqB,EACrB,wBAAwB,EAExB,aAAa,EACd,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,aAAa,EAAE,CAAC;AAS9B;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,OAAO,CAMjF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,IAAI,CAExF;AAED;;GAEG;AACH,wBAAgB,wBAAwB,IAAI,IAAI,CAE/C;AAMD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWtD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,qBAAqB,EAAE,GAC7B,qBAAqB,EAAE,CAiCzB;AAMD;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,MAAM,EACnB,KAAK,CAAC,EAAE,qBAAqB,EAAE,EAC/B,WAAW,CAAC,EAAE,aAAa,GAC1B,OAAO,CAAC,wBAAwB,GAAG,SAAS,CAAC,CAoD/C"}
|
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
// engine/hooks/handlers/on-impl-change.ts — on-impl-change hook handler (SPEC-137)
|
|
2
2
|
// Triggered when source implementation files (src/**/*.ts) change.
|
|
3
|
+
// Analyzes related specs, updates completion percentage, auto-reconciles if all criteria met.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Rate limiter (per spec, 5 min cooldown)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const RATE_LIMIT_MS = 5 * 60 * 1000; // 5 minutes
|
|
8
|
+
const lastAnalysisTimestamps = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Check if the spec is within rate limit window.
|
|
11
|
+
* Returns true if rate-limited (should skip), false if should proceed.
|
|
12
|
+
*/
|
|
13
|
+
export function isRateLimited(specId, nowMs = Date.now()) {
|
|
14
|
+
const last = lastAnalysisTimestamps.get(specId);
|
|
15
|
+
if (last === undefined) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return nowMs - last < RATE_LIMIT_MS;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Record analysis timestamp for a spec.
|
|
22
|
+
*/
|
|
23
|
+
export function recordAnalysisTimestamp(specId, nowMs = Date.now()) {
|
|
24
|
+
lastAnalysisTimestamps.set(specId, nowMs);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clear all rate limit timestamps (for testing).
|
|
28
|
+
*/
|
|
29
|
+
export function clearRateLimitTimestamps() {
|
|
30
|
+
lastAnalysisTimestamps.clear();
|
|
31
|
+
}
|
|
3
32
|
// ---------------------------------------------------------------------------
|
|
4
33
|
// Pattern matching
|
|
5
34
|
// ---------------------------------------------------------------------------
|
|
@@ -19,23 +48,90 @@ export function isSourceFile(filePath) {
|
|
|
19
48
|
}
|
|
20
49
|
return true;
|
|
21
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Find specs related to a changed file by matching file path patterns.
|
|
53
|
+
* Skips specs in terminal states (done, discarded).
|
|
54
|
+
*/
|
|
55
|
+
export function findRelatedSpecs(filePath, specs) {
|
|
56
|
+
const related = [];
|
|
57
|
+
const filePathLower = filePath.toLowerCase();
|
|
58
|
+
for (const spec of specs) {
|
|
59
|
+
// Skip specs in terminal states
|
|
60
|
+
if (spec.status === 'done' || spec.status === 'discarded') {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
// Match by criterion paths (glob patterns declared in the spec)
|
|
64
|
+
if (spec.criterionPaths && spec.criterionPaths.length > 0) {
|
|
65
|
+
const matches = spec.criterionPaths.some((pattern) => {
|
|
66
|
+
const normalized = pattern.toLowerCase().replace(/\*+/g, '');
|
|
67
|
+
return filePathLower.includes(normalized) || normalized.includes(filePathLower);
|
|
68
|
+
});
|
|
69
|
+
if (matches) {
|
|
70
|
+
related.push(spec);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Match by spec slug/id fragments in the file path
|
|
75
|
+
const specSlug = spec.id
|
|
76
|
+
.toLowerCase()
|
|
77
|
+
.replace('spec-', '')
|
|
78
|
+
.replace(/[^a-z0-9]/g, '-');
|
|
79
|
+
if (specSlug.length > 2 && filePathLower.includes(specSlug)) {
|
|
80
|
+
related.push(spec);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return related;
|
|
84
|
+
}
|
|
22
85
|
// ---------------------------------------------------------------------------
|
|
23
86
|
// Handler
|
|
24
87
|
// ---------------------------------------------------------------------------
|
|
25
88
|
/**
|
|
26
89
|
* Handle an implementation file change event.
|
|
27
90
|
* Fire-and-forget — all errors are caught internally.
|
|
91
|
+
* When specs array and analyzeSpec function are provided, analyzes related specs.
|
|
28
92
|
*/
|
|
29
|
-
export function handleOnImplChange(event, projectPath) {
|
|
93
|
+
export async function handleOnImplChange(event, projectPath, specs, analyzeSpec) {
|
|
30
94
|
try {
|
|
31
95
|
if (!isSourceFile(event.path)) {
|
|
32
|
-
return
|
|
96
|
+
return;
|
|
33
97
|
}
|
|
34
98
|
console.warn(`[hooks] Implementation change detected: ${event.path} (${event.type}) in ${projectPath}`);
|
|
99
|
+
if (!specs || specs.length === 0 || !analyzeSpec) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const relatedSpecs = findRelatedSpecs(event.path, specs);
|
|
103
|
+
if (relatedSpecs.length === 0) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const now = Date.now();
|
|
107
|
+
const specsToAnalyze = relatedSpecs.filter((s) => !isRateLimited(s.id, now));
|
|
108
|
+
if (specsToAnalyze.length === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const results = [];
|
|
112
|
+
let autoReconciled = false;
|
|
113
|
+
for (const spec of specsToAnalyze) {
|
|
114
|
+
recordAnalysisTimestamp(spec.id, now);
|
|
115
|
+
const result = await analyzeSpec(spec, projectPath);
|
|
116
|
+
results.push({
|
|
117
|
+
specId: spec.id,
|
|
118
|
+
completionPct: result.completionPct,
|
|
119
|
+
progressUpdated: result.progressUpdated,
|
|
120
|
+
criterionResults: result.criterionResults,
|
|
121
|
+
});
|
|
122
|
+
if (result.completionPct === 100) {
|
|
123
|
+
autoReconciled = true;
|
|
124
|
+
console.warn(`[hooks] Auto-reconcile triggered for ${spec.id} (100% completion)`);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
changedFile: event.path,
|
|
129
|
+
specsAnalyzed: results,
|
|
130
|
+
autoReconciled,
|
|
131
|
+
};
|
|
35
132
|
}
|
|
36
133
|
catch (err) {
|
|
37
134
|
console.warn(`[hooks] on-impl-change error for ${event.path}:`, err);
|
|
38
135
|
}
|
|
39
|
-
return Promise.resolve();
|
|
40
136
|
}
|
|
41
137
|
//# sourceMappingURL=on-impl-change.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-impl-change.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-impl-change.ts"],"names":[],"mappings":"AAAA,mFAAmF;AACnF,mEAAmE;
|
|
1
|
+
{"version":3,"file":"on-impl-change.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-impl-change.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,13 +1,41 @@
|
|
|
1
1
|
import type { WatcherEvent } from '../../../types/file-hooks.js';
|
|
2
|
+
import type { WorkerRunResult } from '../../../types/workers.js';
|
|
3
|
+
/** Default drift threshold: 10% */
|
|
4
|
+
export declare const DEFAULT_DRIFT_THRESHOLD = 0.1;
|
|
2
5
|
/**
|
|
3
6
|
* Returns true if the given path is related to a spec file.
|
|
4
7
|
* Matches planu/specs/** or files matching common spec patterns.
|
|
5
8
|
*/
|
|
6
9
|
export declare function isSpecRelatedFile(path: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Returns true if the given path is security-sensitive.
|
|
12
|
+
* Detects .env files, auth/config/secrets paths, etc.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isSecuritySensitiveFilePath(path: string): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Compute drift score as a ratio of changed spec criteria to total criteria.
|
|
17
|
+
* specCriteria: total number of acceptance criteria
|
|
18
|
+
* fileIndex: number of spec-related changed files
|
|
19
|
+
*/
|
|
20
|
+
export declare function computeDriftScore(specCriteria: number, fileIndex: number): number;
|
|
21
|
+
/**
|
|
22
|
+
* Find security-sensitive paths among changed files.
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectSecuritySensitivePaths(changedFiles: string[]): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Get the cached result from the last push check run.
|
|
27
|
+
*/
|
|
28
|
+
export declare function getLastPushCheckResult(): WorkerRunResult | null;
|
|
29
|
+
/**
|
|
30
|
+
* Reset the push check result cache.
|
|
31
|
+
* Useful for testing.
|
|
32
|
+
*/
|
|
33
|
+
export declare function resetPushCheckCache(): void;
|
|
7
34
|
/**
|
|
8
35
|
* Handle on-push-check event.
|
|
9
|
-
* Performs drift detection
|
|
36
|
+
* Performs drift detection and security-sensitive path detection.
|
|
37
|
+
* Caches results as WorkerRunResult and emits WorkerFindings via console.warn.
|
|
10
38
|
* Fire-and-forget — all errors are caught with console.warn.
|
|
11
39
|
*/
|
|
12
|
-
export declare function handleOnPushCheck(event: WatcherEvent, projectPath: string): Promise<void>;
|
|
40
|
+
export declare function handleOnPushCheck(event: WatcherEvent, projectPath: string, driftThreshold?: number): Promise<void>;
|
|
13
41
|
//# sourceMappingURL=on-push-check.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-push-check.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-push-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"on-push-check.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-push-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAiB,MAAM,2BAA2B,CAAC;AA4BhF,mCAAmC;AACnC,eAAO,MAAM,uBAAuB,MAAM,CAAC;AAY3C;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKvD;AAED;;;GAGG;AACH,wBAAgB,2BAA2B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGjE;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAKjF;AAMD;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAE7E;AAMD;;GAEG;AACH,wBAAgB,sBAAsB,IAAI,eAAe,GAAG,IAAI,CAE/D;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAE1C;AAMD;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,YAAY,EACnB,WAAW,EAAE,MAAM,EACnB,cAAc,SAA0B,GACvC,OAAO,CAAC,IAAI,CAAC,CA4Ef"}
|
|
@@ -11,6 +11,23 @@ const SPEC_FILE_PATTERNS = [
|
|
|
11
11
|
/SPEC-\d+/,
|
|
12
12
|
/\.spec\.(ts|js|tsx|jsx)$/,
|
|
13
13
|
];
|
|
14
|
+
const SECURITY_KEYWORDS = [
|
|
15
|
+
'.env',
|
|
16
|
+
'auth',
|
|
17
|
+
'config',
|
|
18
|
+
'secret',
|
|
19
|
+
'credential',
|
|
20
|
+
'password',
|
|
21
|
+
'token',
|
|
22
|
+
'key',
|
|
23
|
+
'private',
|
|
24
|
+
];
|
|
25
|
+
/** Default drift threshold: 10% */
|
|
26
|
+
export const DEFAULT_DRIFT_THRESHOLD = 0.1;
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Module-level cache
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
let lastPushCheckResult = null;
|
|
14
31
|
// ---------------------------------------------------------------------------
|
|
15
32
|
// Path classification
|
|
16
33
|
// ---------------------------------------------------------------------------
|
|
@@ -24,24 +41,122 @@ export function isSpecRelatedFile(path) {
|
|
|
24
41
|
}
|
|
25
42
|
return SPEC_FILE_PATTERNS.some((pattern) => pattern.test(path));
|
|
26
43
|
}
|
|
44
|
+
/**
|
|
45
|
+
* Returns true if the given path is security-sensitive.
|
|
46
|
+
* Detects .env files, auth/config/secrets paths, etc.
|
|
47
|
+
*/
|
|
48
|
+
export function isSecuritySensitiveFilePath(path) {
|
|
49
|
+
const lower = path.toLowerCase();
|
|
50
|
+
return SECURITY_KEYWORDS.some((keyword) => lower.includes(keyword));
|
|
51
|
+
}
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Drift computation
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
/**
|
|
56
|
+
* Compute drift score as a ratio of changed spec criteria to total criteria.
|
|
57
|
+
* specCriteria: total number of acceptance criteria
|
|
58
|
+
* fileIndex: number of spec-related changed files
|
|
59
|
+
*/
|
|
60
|
+
export function computeDriftScore(specCriteria, fileIndex) {
|
|
61
|
+
if (specCriteria <= 0) {
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
64
|
+
return Math.min(1, fileIndex / specCriteria);
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Security detection
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
/**
|
|
70
|
+
* Find security-sensitive paths among changed files.
|
|
71
|
+
*/
|
|
72
|
+
export function detectSecuritySensitivePaths(changedFiles) {
|
|
73
|
+
return changedFiles.filter((f) => isSecuritySensitiveFilePath(f));
|
|
74
|
+
}
|
|
75
|
+
// ---------------------------------------------------------------------------
|
|
76
|
+
// Cache accessors
|
|
77
|
+
// ---------------------------------------------------------------------------
|
|
78
|
+
/**
|
|
79
|
+
* Get the cached result from the last push check run.
|
|
80
|
+
*/
|
|
81
|
+
export function getLastPushCheckResult() {
|
|
82
|
+
return lastPushCheckResult;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Reset the push check result cache.
|
|
86
|
+
* Useful for testing.
|
|
87
|
+
*/
|
|
88
|
+
export function resetPushCheckCache() {
|
|
89
|
+
lastPushCheckResult = null;
|
|
90
|
+
}
|
|
27
91
|
// ---------------------------------------------------------------------------
|
|
28
92
|
// Handler
|
|
29
93
|
// ---------------------------------------------------------------------------
|
|
30
94
|
/**
|
|
31
95
|
* Handle on-push-check event.
|
|
32
|
-
* Performs drift detection
|
|
96
|
+
* Performs drift detection and security-sensitive path detection.
|
|
97
|
+
* Caches results as WorkerRunResult and emits WorkerFindings via console.warn.
|
|
33
98
|
* Fire-and-forget — all errors are caught with console.warn.
|
|
34
99
|
*/
|
|
35
|
-
export function handleOnPushCheck(event, projectPath) {
|
|
100
|
+
export function handleOnPushCheck(event, projectPath, driftThreshold = DEFAULT_DRIFT_THRESHOLD) {
|
|
101
|
+
const startedAt = new Date().toISOString();
|
|
102
|
+
const startMs = Date.now();
|
|
36
103
|
try {
|
|
37
104
|
const specRelated = isSpecRelatedFile(event.path);
|
|
38
105
|
console.warn(`[hooks] on-push-check: path=${event.path} type=${event.type} specRelated=${String(specRelated)} project=${projectPath}`);
|
|
106
|
+
const findings = [];
|
|
107
|
+
const changedFiles = [event.path];
|
|
108
|
+
// Drift detection
|
|
39
109
|
if (specRelated) {
|
|
40
110
|
console.warn(`[hooks] on-push-check: spec-related file detected, drift check triggered for ${event.path}`);
|
|
111
|
+
const driftScore = computeDriftScore(10, changedFiles.filter(isSpecRelatedFile).length);
|
|
112
|
+
if (driftScore >= driftThreshold) {
|
|
113
|
+
const driftPct = Math.round(driftScore * 100);
|
|
114
|
+
findings.push({
|
|
115
|
+
severity: 'warning',
|
|
116
|
+
file: event.path,
|
|
117
|
+
message: `Drift detected: ${String(driftPct)}% of spec criteria affected`,
|
|
118
|
+
suggestion: 'Review and reconcile spec documentation',
|
|
119
|
+
category: 'drift',
|
|
120
|
+
});
|
|
121
|
+
console.warn(`[hooks] on-push-check: drift threshold exceeded (${String(driftPct)}%) for ${event.path}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
// Security detection
|
|
125
|
+
const sensitivePaths = detectSecuritySensitivePaths(changedFiles);
|
|
126
|
+
for (const sensitivePath of sensitivePaths) {
|
|
127
|
+
findings.push({
|
|
128
|
+
severity: 'high',
|
|
129
|
+
file: sensitivePath,
|
|
130
|
+
message: `Security-sensitive file changed: ${sensitivePath}`,
|
|
131
|
+
suggestion: 'Review changes to security-sensitive files carefully',
|
|
132
|
+
category: 'security',
|
|
133
|
+
});
|
|
134
|
+
console.warn(`[hooks] on-push-check: security-sensitive file detected: ${sensitivePath}`);
|
|
41
135
|
}
|
|
136
|
+
const completedAt = new Date().toISOString();
|
|
137
|
+
lastPushCheckResult = {
|
|
138
|
+
workerName: 'on-push-check',
|
|
139
|
+
startedAt,
|
|
140
|
+
completedAt,
|
|
141
|
+
durationMs: Date.now() - startMs,
|
|
142
|
+
status: 'success',
|
|
143
|
+
findings,
|
|
144
|
+
analyzedFiles: changedFiles.length,
|
|
145
|
+
};
|
|
42
146
|
}
|
|
43
147
|
catch (err) {
|
|
44
148
|
console.warn(`[hooks] on-push-check: unexpected error for ${event.path}`, err);
|
|
149
|
+
const completedAt = new Date().toISOString();
|
|
150
|
+
lastPushCheckResult = {
|
|
151
|
+
workerName: 'on-push-check',
|
|
152
|
+
startedAt,
|
|
153
|
+
completedAt,
|
|
154
|
+
durationMs: Date.now() - startMs,
|
|
155
|
+
status: 'error',
|
|
156
|
+
findings: [],
|
|
157
|
+
error: String(err),
|
|
158
|
+
analyzedFiles: 0,
|
|
159
|
+
};
|
|
45
160
|
}
|
|
46
161
|
return Promise.resolve();
|
|
47
162
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-push-check.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-push-check.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,8DAA8D;
|
|
1
|
+
{"version":3,"file":"on-push-check.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-push-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,16 +1,44 @@
|
|
|
1
1
|
import type { WatcherEvent } from '../../../types/file-hooks.js';
|
|
2
|
+
import type { WorkerRunResult, WorkerFinding } from '../../../types/workers.js';
|
|
2
3
|
/**
|
|
3
4
|
* Returns true if the given path contains security-sensitive keywords.
|
|
4
5
|
*/
|
|
5
6
|
export declare function isSecuritySensitiveFile(path: string): boolean;
|
|
7
|
+
/**
|
|
8
|
+
* Returns true if the given path is a dependency manifest or lockfile.
|
|
9
|
+
*/
|
|
10
|
+
export declare function isDependencyFile(path: string): boolean;
|
|
11
|
+
/**
|
|
12
|
+
* Returns true if the security check is currently in cooldown.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isInCooldown(): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* Returns remaining cooldown time in seconds (0 if not in cooldown).
|
|
17
|
+
*/
|
|
18
|
+
export declare function getCooldownRemainingSeconds(): number;
|
|
6
19
|
/**
|
|
7
20
|
* Reset the security cooldown timestamp.
|
|
8
21
|
* Useful for testing.
|
|
9
22
|
*/
|
|
10
23
|
export declare function resetSecurityCooldown(): void;
|
|
24
|
+
/**
|
|
25
|
+
* Build WorkerFindings for dependency file changes.
|
|
26
|
+
*/
|
|
27
|
+
export declare function buildDependencyHealthFindings(depFiles: string[]): WorkerFinding[];
|
|
28
|
+
/**
|
|
29
|
+
* Get the cached result from the last security check run.
|
|
30
|
+
*/
|
|
31
|
+
export declare function getLastSecurityCheckResult(): WorkerRunResult | null;
|
|
32
|
+
/**
|
|
33
|
+
* Reset the security check result cache.
|
|
34
|
+
* Useful for testing.
|
|
35
|
+
*/
|
|
36
|
+
export declare function resetSecurityCheckCache(): void;
|
|
11
37
|
/**
|
|
12
38
|
* Handle on-security-check event for security-sensitive file changes.
|
|
13
39
|
* Implements a 5-minute cooldown to avoid repeated alerts.
|
|
40
|
+
* Also detects dependency file changes and emits dependency-health findings.
|
|
41
|
+
* Caches results as WorkerRunResult.
|
|
14
42
|
* Fire-and-forget — all errors are caught with console.warn.
|
|
15
43
|
*/
|
|
16
44
|
export declare function handleOnSecurityCheck(event: WatcherEvent, projectPath: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-security-check.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-security-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"on-security-check.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-security-check.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AA+ChF;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGtD;AAMD;;GAEG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAKtC;AAED;;GAEG;AACH,wBAAgB,2BAA2B,IAAI,MAAM,CAMpD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAMD;;GAEG;AACH,wBAAgB,6BAA6B,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,aAAa,EAAE,CAQjF;AAMD;;GAEG;AACH,wBAAgB,0BAA0B,IAAI,eAAe,GAAG,IAAI,CAEnE;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,IAAI,IAAI,CAE9C;AAMD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA6D7F"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// engine/hooks/handlers/on-security-check.ts — on-security-check hook handler (SPEC-138)
|
|
2
|
-
// Security-sensitive file change detection with cooldown.
|
|
2
|
+
// Security-sensitive file change detection with cooldown and dependency health.
|
|
3
3
|
// ---------------------------------------------------------------------------
|
|
4
4
|
// Constants
|
|
5
5
|
// ---------------------------------------------------------------------------
|
|
@@ -12,12 +12,29 @@ const SECURITY_KEYWORDS = [
|
|
|
12
12
|
'key',
|
|
13
13
|
'license',
|
|
14
14
|
];
|
|
15
|
+
const DEPENDENCY_FILES = [
|
|
16
|
+
'package.json',
|
|
17
|
+
'package-lock.json',
|
|
18
|
+
'yarn.lock',
|
|
19
|
+
'pnpm-lock.yaml',
|
|
20
|
+
'bun.lockb',
|
|
21
|
+
'Gemfile',
|
|
22
|
+
'Gemfile.lock',
|
|
23
|
+
'requirements.txt',
|
|
24
|
+
'Pipfile',
|
|
25
|
+
'Pipfile.lock',
|
|
26
|
+
'go.mod',
|
|
27
|
+
'go.sum',
|
|
28
|
+
'Cargo.toml',
|
|
29
|
+
'Cargo.lock',
|
|
30
|
+
];
|
|
15
31
|
/** Cooldown window: 5 minutes in milliseconds */
|
|
16
32
|
const SECURITY_COOLDOWN_MS = 5 * 60 * 1000;
|
|
17
33
|
// ---------------------------------------------------------------------------
|
|
18
|
-
//
|
|
34
|
+
// Module-level state
|
|
19
35
|
// ---------------------------------------------------------------------------
|
|
20
36
|
let lastSecurityCheckAt = 0;
|
|
37
|
+
let lastSecurityCheckResult = null;
|
|
21
38
|
// ---------------------------------------------------------------------------
|
|
22
39
|
// Path classification
|
|
23
40
|
// ---------------------------------------------------------------------------
|
|
@@ -28,9 +45,35 @@ export function isSecuritySensitiveFile(path) {
|
|
|
28
45
|
const lower = path.toLowerCase();
|
|
29
46
|
return SECURITY_KEYWORDS.some((keyword) => lower.includes(keyword));
|
|
30
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns true if the given path is a dependency manifest or lockfile.
|
|
50
|
+
*/
|
|
51
|
+
export function isDependencyFile(path) {
|
|
52
|
+
const basename = path.split('/').pop() ?? path;
|
|
53
|
+
return DEPENDENCY_FILES.some((dep) => basename === dep);
|
|
54
|
+
}
|
|
31
55
|
// ---------------------------------------------------------------------------
|
|
32
56
|
// Cooldown management
|
|
33
57
|
// ---------------------------------------------------------------------------
|
|
58
|
+
/**
|
|
59
|
+
* Returns true if the security check is currently in cooldown.
|
|
60
|
+
*/
|
|
61
|
+
export function isInCooldown() {
|
|
62
|
+
if (lastSecurityCheckAt === 0) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return Date.now() - lastSecurityCheckAt < SECURITY_COOLDOWN_MS;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Returns remaining cooldown time in seconds (0 if not in cooldown).
|
|
69
|
+
*/
|
|
70
|
+
export function getCooldownRemainingSeconds() {
|
|
71
|
+
if (!isInCooldown()) {
|
|
72
|
+
return 0;
|
|
73
|
+
}
|
|
74
|
+
const elapsed = Date.now() - lastSecurityCheckAt;
|
|
75
|
+
return Math.max(0, Math.round((SECURITY_COOLDOWN_MS - elapsed) / 1000));
|
|
76
|
+
}
|
|
34
77
|
/**
|
|
35
78
|
* Reset the security cooldown timestamp.
|
|
36
79
|
* Useful for testing.
|
|
@@ -39,30 +82,107 @@ export function resetSecurityCooldown() {
|
|
|
39
82
|
lastSecurityCheckAt = 0;
|
|
40
83
|
}
|
|
41
84
|
// ---------------------------------------------------------------------------
|
|
85
|
+
// Dependency health findings
|
|
86
|
+
// ---------------------------------------------------------------------------
|
|
87
|
+
/**
|
|
88
|
+
* Build WorkerFindings for dependency file changes.
|
|
89
|
+
*/
|
|
90
|
+
export function buildDependencyHealthFindings(depFiles) {
|
|
91
|
+
return depFiles.map((file) => ({
|
|
92
|
+
severity: 'info',
|
|
93
|
+
file,
|
|
94
|
+
message: `Dependency file changed: ${file}`,
|
|
95
|
+
suggestion: 'Review dependency changes for security vulnerabilities or breaking updates',
|
|
96
|
+
category: 'dependency-health',
|
|
97
|
+
}));
|
|
98
|
+
}
|
|
99
|
+
// ---------------------------------------------------------------------------
|
|
100
|
+
// Cache accessors
|
|
101
|
+
// ---------------------------------------------------------------------------
|
|
102
|
+
/**
|
|
103
|
+
* Get the cached result from the last security check run.
|
|
104
|
+
*/
|
|
105
|
+
export function getLastSecurityCheckResult() {
|
|
106
|
+
return lastSecurityCheckResult;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Reset the security check result cache.
|
|
110
|
+
* Useful for testing.
|
|
111
|
+
*/
|
|
112
|
+
export function resetSecurityCheckCache() {
|
|
113
|
+
lastSecurityCheckResult = null;
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
42
116
|
// Handler
|
|
43
117
|
// ---------------------------------------------------------------------------
|
|
44
118
|
/**
|
|
45
119
|
* Handle on-security-check event for security-sensitive file changes.
|
|
46
120
|
* Implements a 5-minute cooldown to avoid repeated alerts.
|
|
121
|
+
* Also detects dependency file changes and emits dependency-health findings.
|
|
122
|
+
* Caches results as WorkerRunResult.
|
|
47
123
|
* Fire-and-forget — all errors are caught with console.warn.
|
|
48
124
|
*/
|
|
49
125
|
export function handleOnSecurityCheck(event, projectPath) {
|
|
126
|
+
const startedAt = new Date().toISOString();
|
|
127
|
+
const startMs = Date.now();
|
|
50
128
|
try {
|
|
129
|
+
const findings = [];
|
|
130
|
+
const changedFiles = [event.path];
|
|
131
|
+
// Dependency health check (always runs, no cooldown)
|
|
132
|
+
const depFiles = changedFiles.filter((f) => isDependencyFile(f));
|
|
133
|
+
if (depFiles.length > 0) {
|
|
134
|
+
const depFindings = buildDependencyHealthFindings(depFiles);
|
|
135
|
+
findings.push(...depFindings);
|
|
136
|
+
for (const depFile of depFiles) {
|
|
137
|
+
console.warn(`[hooks] on-security-check: dependency file changed: ${depFile} project=${projectPath}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Security-sensitive file check (with cooldown)
|
|
51
141
|
if (!isSecuritySensitiveFile(event.path)) {
|
|
142
|
+
// Still cache result if dependency findings exist
|
|
143
|
+
if (findings.length > 0) {
|
|
144
|
+
lastSecurityCheckResult = buildRunResult(startedAt, startMs, 'success', findings, 1);
|
|
145
|
+
}
|
|
52
146
|
return Promise.resolve();
|
|
53
147
|
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
148
|
+
if (isInCooldown()) {
|
|
149
|
+
const remaining = getCooldownRemainingSeconds();
|
|
150
|
+
console.warn(`[hooks] on-security-check: cooldown active, skipping check for ${event.path} (${String(remaining)}s remaining)`);
|
|
151
|
+
if (findings.length > 0) {
|
|
152
|
+
lastSecurityCheckResult = buildRunResult(startedAt, startMs, 'skipped', findings, 1);
|
|
153
|
+
}
|
|
58
154
|
return Promise.resolve();
|
|
59
155
|
}
|
|
60
|
-
lastSecurityCheckAt = now;
|
|
156
|
+
lastSecurityCheckAt = Date.now();
|
|
61
157
|
console.warn(`[hooks] on-security-check: security check triggered for ${event.path} type=${event.type} project=${projectPath}`);
|
|
158
|
+
findings.push({
|
|
159
|
+
severity: 'high',
|
|
160
|
+
file: event.path,
|
|
161
|
+
message: `Security-sensitive file changed: ${event.path}`,
|
|
162
|
+
suggestion: 'Review security-sensitive file changes carefully before merging',
|
|
163
|
+
category: 'security',
|
|
164
|
+
});
|
|
165
|
+
lastSecurityCheckResult = buildRunResult(startedAt, startMs, 'success', findings, 1);
|
|
62
166
|
}
|
|
63
167
|
catch (err) {
|
|
64
168
|
console.warn(`[hooks] on-security-check: unexpected error for ${event.path}`, err);
|
|
169
|
+
lastSecurityCheckResult = buildRunResult(startedAt, startMs, 'error', [], 0, String(err));
|
|
65
170
|
}
|
|
66
171
|
return Promise.resolve();
|
|
67
172
|
}
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Internal helpers
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
function buildRunResult(startedAt, startMs, status, findings, analyzedFiles, error) {
|
|
177
|
+
return {
|
|
178
|
+
workerName: 'on-security-check',
|
|
179
|
+
startedAt,
|
|
180
|
+
completedAt: new Date().toISOString(),
|
|
181
|
+
durationMs: Date.now() - startMs,
|
|
182
|
+
status,
|
|
183
|
+
findings,
|
|
184
|
+
analyzedFiles,
|
|
185
|
+
...(error !== undefined ? { error } : {}),
|
|
186
|
+
};
|
|
187
|
+
}
|
|
68
188
|
//# sourceMappingURL=on-security-check.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"on-security-check.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-security-check.ts"],"names":[],"mappings":"AAAA,yFAAyF;AACzF,
|
|
1
|
+
{"version":3,"file":"on-security-check.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-security-check.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { EventBusEvent, SpecStatusChangePayload, StatusChangeResult, DashboardRegenFn } from '../../../types/file-hooks.js';
|
|
2
|
+
import type { EventBus } from '../event-bus.js';
|
|
3
|
+
/**
|
|
4
|
+
* Check if dashboard regen for the project is within rate limit.
|
|
5
|
+
* Returns true if rate-limited (should skip), false if should proceed.
|
|
6
|
+
*/
|
|
7
|
+
export declare function isDashboardRateLimited(projectId: string, nowMs?: number): boolean;
|
|
8
|
+
/**
|
|
9
|
+
* Record regen timestamp for a project.
|
|
10
|
+
*/
|
|
11
|
+
export declare function recordRegenTimestamp(projectId: string, nowMs?: number): void;
|
|
12
|
+
/**
|
|
13
|
+
* Clear all regen timestamps (for testing).
|
|
14
|
+
*/
|
|
15
|
+
export declare function clearRegenTimestamps(): void;
|
|
16
|
+
/**
|
|
17
|
+
* Extract and validate SpecStatusChangePayload from an EventBusEvent.
|
|
18
|
+
* Returns null if payload is invalid or missing required fields.
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractStatusChangePayload(event: EventBusEvent): SpecStatusChangePayload | null;
|
|
21
|
+
/**
|
|
22
|
+
* Handle a spec:status-change event.
|
|
23
|
+
* Regenerates the spec dashboard, rate-limited per project.
|
|
24
|
+
*/
|
|
25
|
+
export declare function handleStatusChange(event: EventBusEvent, regenFn: DashboardRegenFn): Promise<StatusChangeResult | null>;
|
|
26
|
+
/**
|
|
27
|
+
* Emit a spec:status-change event on the event bus.
|
|
28
|
+
*/
|
|
29
|
+
export declare function emitSpecStatusChange(bus: EventBus, payload: SpecStatusChangePayload): EventBusEvent;
|
|
30
|
+
/**
|
|
31
|
+
* Subscribe to spec:status-change events on the event bus.
|
|
32
|
+
* Returns an unsubscribe function.
|
|
33
|
+
*/
|
|
34
|
+
export declare function subscribeStatusChangeHandler(bus: EventBus, regenFn: DashboardRegenFn): () => void;
|
|
35
|
+
//# sourceMappingURL=on-status-change.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-status-change.d.ts","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-status-change.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EACV,aAAa,EACb,uBAAuB,EACvB,kBAAkB,EAClB,gBAAgB,EACjB,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAShD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,OAAO,CAM7F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,GAAE,MAAmB,GAAG,IAAI,CAExF;AAED;;GAEG;AACH,wBAAgB,oBAAoB,IAAI,IAAI,CAE3C;AAMD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,aAAa,GAAG,uBAAuB,GAAG,IAAI,CA0B/F;AAMD;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,gBAAgB,GACxB,OAAO,CAAC,kBAAkB,GAAG,IAAI,CAAC,CAoCpC;AAMD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,QAAQ,EACb,OAAO,EAAE,uBAAuB,GAC/B,aAAa,CAQf;AAED;;;GAGG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAUjG"}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
// engine/hooks/handlers/on-status-change.ts — on-status-change hook handler (SPEC-137)
|
|
2
|
+
// Listens for spec:status-change events and regenerates the spec dashboard.
|
|
3
|
+
// Rate-limited to 10 seconds per project to avoid excessive regeneration.
|
|
4
|
+
// ---------------------------------------------------------------------------
|
|
5
|
+
// Rate limiter (per project, 10 second cooldown)
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
const RATE_LIMIT_MS = 10_000; // 10 seconds
|
|
8
|
+
const lastRegenTimestamps = new Map();
|
|
9
|
+
/**
|
|
10
|
+
* Check if dashboard regen for the project is within rate limit.
|
|
11
|
+
* Returns true if rate-limited (should skip), false if should proceed.
|
|
12
|
+
*/
|
|
13
|
+
export function isDashboardRateLimited(projectId, nowMs = Date.now()) {
|
|
14
|
+
const last = lastRegenTimestamps.get(projectId);
|
|
15
|
+
if (last === undefined) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
return nowMs - last < RATE_LIMIT_MS;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Record regen timestamp for a project.
|
|
22
|
+
*/
|
|
23
|
+
export function recordRegenTimestamp(projectId, nowMs = Date.now()) {
|
|
24
|
+
lastRegenTimestamps.set(projectId, nowMs);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Clear all regen timestamps (for testing).
|
|
28
|
+
*/
|
|
29
|
+
export function clearRegenTimestamps() {
|
|
30
|
+
lastRegenTimestamps.clear();
|
|
31
|
+
}
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
// Payload extraction
|
|
34
|
+
// ---------------------------------------------------------------------------
|
|
35
|
+
/**
|
|
36
|
+
* Extract and validate SpecStatusChangePayload from an EventBusEvent.
|
|
37
|
+
* Returns null if payload is invalid or missing required fields.
|
|
38
|
+
*/
|
|
39
|
+
export function extractStatusChangePayload(event) {
|
|
40
|
+
const { data } = event;
|
|
41
|
+
if (typeof data.eventName !== 'string' || data.eventName !== 'spec:status-change') {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (typeof data.specId !== 'string' ||
|
|
45
|
+
typeof data.oldStatus !== 'string' ||
|
|
46
|
+
typeof data.newStatus !== 'string' ||
|
|
47
|
+
typeof data.projectId !== 'string') {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
if (!data.specId || !data.projectId) {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
return {
|
|
54
|
+
specId: data.specId,
|
|
55
|
+
oldStatus: data.oldStatus,
|
|
56
|
+
newStatus: data.newStatus,
|
|
57
|
+
projectId: data.projectId,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Core handler logic
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
/**
|
|
64
|
+
* Handle a spec:status-change event.
|
|
65
|
+
* Regenerates the spec dashboard, rate-limited per project.
|
|
66
|
+
*/
|
|
67
|
+
export async function handleStatusChange(event, regenFn) {
|
|
68
|
+
const payload = extractStatusChangePayload(event);
|
|
69
|
+
if (!payload) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
const now = Date.now();
|
|
73
|
+
if (isDashboardRateLimited(payload.projectId, now)) {
|
|
74
|
+
return {
|
|
75
|
+
specId: payload.specId,
|
|
76
|
+
dashboardRegenerated: false,
|
|
77
|
+
reason: 'rate-limited',
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
recordRegenTimestamp(payload.projectId, now);
|
|
81
|
+
try {
|
|
82
|
+
const regenerated = await regenFn(payload.projectId);
|
|
83
|
+
if (regenerated) {
|
|
84
|
+
console.warn(`[hooks] Dashboard regenerated for project ${payload.projectId} after ${payload.specId} changed to ${payload.newStatus}`);
|
|
85
|
+
}
|
|
86
|
+
return {
|
|
87
|
+
specId: payload.specId,
|
|
88
|
+
dashboardRegenerated: regenerated,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
console.warn(`[hooks] on-status-change dashboard regen error:`, err);
|
|
93
|
+
return {
|
|
94
|
+
specId: payload.specId,
|
|
95
|
+
dashboardRegenerated: false,
|
|
96
|
+
reason: 'regen-error',
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// EventBus integration
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
/**
|
|
104
|
+
* Emit a spec:status-change event on the event bus.
|
|
105
|
+
*/
|
|
106
|
+
export function emitSpecStatusChange(bus, payload) {
|
|
107
|
+
return bus.emitEvent('hook:complete', {
|
|
108
|
+
eventName: 'spec:status-change',
|
|
109
|
+
specId: payload.specId,
|
|
110
|
+
oldStatus: payload.oldStatus,
|
|
111
|
+
newStatus: payload.newStatus,
|
|
112
|
+
projectId: payload.projectId,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Subscribe to spec:status-change events on the event bus.
|
|
117
|
+
* Returns an unsubscribe function.
|
|
118
|
+
*/
|
|
119
|
+
export function subscribeStatusChangeHandler(bus, regenFn) {
|
|
120
|
+
const listener = (event) => {
|
|
121
|
+
void handleStatusChange(event, regenFn);
|
|
122
|
+
};
|
|
123
|
+
bus.onEvent('hook:complete', listener);
|
|
124
|
+
return () => {
|
|
125
|
+
bus.off('hook:complete', listener);
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=on-status-change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"on-status-change.js","sourceRoot":"","sources":["../../../../src/engine/hooks/handlers/on-status-change.ts"],"names":[],"mappings":"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"}
|
|
@@ -128,6 +128,8 @@ export interface OnCommitHookResult {
|
|
|
128
128
|
valid: boolean;
|
|
129
129
|
errors: string[];
|
|
130
130
|
};
|
|
131
|
+
/** Drift warnings when spec changes exceed threshold (SPEC-138) */
|
|
132
|
+
driftWarnings?: string[];
|
|
131
133
|
}
|
|
132
134
|
/** Context for on-commit handler */
|
|
133
135
|
export interface OnCommitContext {
|
|
@@ -201,6 +203,84 @@ export interface FileCoverage {
|
|
|
201
203
|
}
|
|
202
204
|
export type CoverageSummary = Record<string, FileCoverage>;
|
|
203
205
|
export type ExtendedHookResult = FileHookResult | OnTestPassHookResult | OnCommitHookResult;
|
|
206
|
+
/** Minimal spec info needed to run living-spec analysis without loading the full store */
|
|
207
|
+
export interface MinimalAnalyzableSpec {
|
|
208
|
+
/** Spec ID (e.g. "SPEC-042") */
|
|
209
|
+
id: string;
|
|
210
|
+
/** Spec title */
|
|
211
|
+
title: string;
|
|
212
|
+
/** Current status */
|
|
213
|
+
status: string;
|
|
214
|
+
/** Absolute path to progress.md (optional) */
|
|
215
|
+
progressPath?: string;
|
|
216
|
+
/** Glob patterns of files in the spec implementation plan */
|
|
217
|
+
criterionPaths?: string[];
|
|
218
|
+
}
|
|
219
|
+
/** Result of analyzing a single criterion after an implementation change */
|
|
220
|
+
export interface ImplChangeCriterionResult {
|
|
221
|
+
/** Criterion text */
|
|
222
|
+
criterion: string;
|
|
223
|
+
/** Whether criterion was met */
|
|
224
|
+
met: boolean;
|
|
225
|
+
/** Evidence found (or reason for not met) */
|
|
226
|
+
evidence: string;
|
|
227
|
+
}
|
|
228
|
+
/** Per-spec result after analyzing implementation changes */
|
|
229
|
+
export interface ImplChangeSpecResult {
|
|
230
|
+
/** Spec ID */
|
|
231
|
+
specId: string;
|
|
232
|
+
/** Completion percentage 0-100 */
|
|
233
|
+
completionPct: number;
|
|
234
|
+
/** Whether progress.md was updated */
|
|
235
|
+
progressUpdated: boolean;
|
|
236
|
+
/** Criterion-level results */
|
|
237
|
+
criterionResults: ImplChangeCriterionResult[];
|
|
238
|
+
}
|
|
239
|
+
/** Result of handling an impl-change event */
|
|
240
|
+
export interface ImplChangeAnalysisResult {
|
|
241
|
+
/** Path that triggered the analysis */
|
|
242
|
+
changedFile: string;
|
|
243
|
+
/** Specs analyzed */
|
|
244
|
+
specsAnalyzed: ImplChangeSpecResult[];
|
|
245
|
+
/** Whether any spec reached 100% and was auto-reconciled */
|
|
246
|
+
autoReconciled: boolean;
|
|
247
|
+
}
|
|
248
|
+
/** Payload emitted when a spec status changes */
|
|
249
|
+
export interface SpecStatusChangePayload {
|
|
250
|
+
/** Spec ID that changed status */
|
|
251
|
+
specId: string;
|
|
252
|
+
/** Previous status */
|
|
253
|
+
oldStatus: string;
|
|
254
|
+
/** New status */
|
|
255
|
+
newStatus: string;
|
|
256
|
+
/** Project identifier (hash or path) */
|
|
257
|
+
projectId: string;
|
|
258
|
+
}
|
|
259
|
+
/** Result of handling a spec status change event */
|
|
260
|
+
export interface StatusChangeResult {
|
|
261
|
+
/** Spec ID processed */
|
|
262
|
+
specId: string;
|
|
263
|
+
/** Whether the dashboard was regenerated */
|
|
264
|
+
dashboardRegenerated: boolean;
|
|
265
|
+
/** Reason for not regenerating (if applicable) */
|
|
266
|
+
reason?: string;
|
|
267
|
+
}
|
|
268
|
+
/** Function type for triggering dashboard regeneration */
|
|
269
|
+
export type DashboardRegenFn = (projectId: string) => Promise<boolean>;
|
|
270
|
+
/** Per-criterion result returned by AnalyzeSpecFn */
|
|
271
|
+
export interface AnalyzeCriterionResult {
|
|
272
|
+
criterion: string;
|
|
273
|
+
met: boolean;
|
|
274
|
+
evidence: string;
|
|
275
|
+
}
|
|
276
|
+
/** Return type for AnalyzeSpecFn */
|
|
277
|
+
export interface AnalyzeSpecFnResult {
|
|
278
|
+
completionPct: number;
|
|
279
|
+
progressUpdated: boolean;
|
|
280
|
+
criterionResults: AnalyzeCriterionResult[];
|
|
281
|
+
}
|
|
282
|
+
/** Function that analyzes a MinimalAnalyzableSpec and returns completion data */
|
|
283
|
+
export type AnalyzeSpecFn = (spec: MinimalAnalyzableSpec, projectPath: string) => Promise<AnalyzeSpecFnResult>;
|
|
204
284
|
export interface StartHooksInput {
|
|
205
285
|
/** Absolute path to the project root directory to watch */
|
|
206
286
|
projectPath: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file-hooks.d.ts","sourceRoot":"","sources":["../../src/types/file-hooks.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"file-hooks.d.ts","sourceRoot":"","sources":["../../src/types/file-hooks.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.54.0",
|
|
4
4
|
"description": "Planu — MCP Server for Spec Driven Development. Manages specs, estimations, reverse engineering, and auto-learning across any language/framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|