@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.
@@ -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;AAgBxF;;;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,CAgCtF"}
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":"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"}
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<void>;
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":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,8BAA8B,CAAC;AAMjE;;;GAGG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAWtD;AAMD;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAa1F"}
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 Promise.resolve();
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;AAInE,8EAA8E;AAC9E,mBAAmB;AACnB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACjC,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC;QACnE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,KAAmB,EAAE,WAAmB;IACzE,IAAI,CAAC;QACH,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9B,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,OAAO,CAAC,IAAI,CACV,2CAA2C,KAAK,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,QAAQ,WAAW,EAAE,CAC1F,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,oCAAoC,KAAK,CAAC,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;AAC3B,CAAC"}
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 logging for spec-related files.
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;AAoBjE;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAKvD;AAMD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAiBzF"}
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 logging for spec-related files.
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;AAI9D,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,gBAAgB,GAAG,cAAc,CAAC;AAExC,MAAM,kBAAkB,GAAa;IACnC,WAAW;IACX,gBAAgB;IAChB,eAAe;IACf,UAAU;IACV,0BAA0B;CAC3B,CAAC;AAEF,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,IAAI,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;QACtC,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,kBAAkB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;AAClE,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAmB,EAAE,WAAmB;IACxE,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAElD,OAAO,CAAC,IAAI,CACV,+BAA+B,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,IAAI,gBAAgB,MAAM,CAAC,WAAW,CAAC,YAAY,WAAW,EAAE,CACzH,CAAC;QAEF,IAAI,WAAW,EAAE,CAAC;YAChB,OAAO,CAAC,IAAI,CACV,gFAAgF,KAAK,CAAC,IAAI,EAAE,CAC7F,CAAC;QACJ,CAAC;IACH,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,+CAA+C,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IACjF,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;AAC3B,CAAC"}
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;AA6BjE;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAG7D;AAMD;;;GAGG;AACH,wBAAgB,qBAAqB,IAAI,IAAI,CAE5C;AAMD;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyB7F"}
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
- // Cooldown state (module-level)
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
- const now = Date.now();
55
- const elapsed = now - lastSecurityCheckAt;
56
- if (lastSecurityCheckAt > 0 && elapsed < SECURITY_COOLDOWN_MS) {
57
- console.warn(`[hooks] on-security-check: cooldown active, skipping check for ${event.path} (${String(Math.round((SECURITY_COOLDOWN_MS - elapsed) / 1000))}s remaining)`);
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,0DAA0D;AAI1D,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,iBAAiB,GAAa;IAClC,MAAM;IACN,OAAO;IACP,QAAQ;IACR,UAAU;IACV,YAAY;IACZ,KAAK;IACL,SAAS;CACV,CAAC;AAEF,iDAAiD;AACjD,MAAM,oBAAoB,GAAG,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC;AAE3C,8EAA8E;AAC9E,gCAAgC;AAChC,8EAA8E;AAE9E,IAAI,mBAAmB,GAAG,CAAC,CAAC;AAE5B,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;GAEG;AACH,MAAM,UAAU,uBAAuB,CAAC,IAAY;IAClD,MAAM,KAAK,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IACjC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,8EAA8E;AAC9E,sBAAsB;AACtB,8EAA8E;AAE9E;;;GAGG;AACH,MAAM,UAAU,qBAAqB;IACnC,mBAAmB,GAAG,CAAC,CAAC;AAC1B,CAAC;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAmB,EAAE,WAAmB;IAC5E,IAAI,CAAC;QACH,IAAI,CAAC,uBAAuB,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACzC,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,GAAG,GAAG,mBAAmB,CAAC;QAE1C,IAAI,mBAAmB,GAAG,CAAC,IAAI,OAAO,GAAG,oBAAoB,EAAE,CAAC;YAC9D,OAAO,CAAC,IAAI,CACV,kEAAkE,KAAK,CAAC,IAAI,KAAK,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,oBAAoB,GAAG,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC,cAAc,CAC3J,CAAC;YACF,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAED,mBAAmB,GAAG,GAAG,CAAC;QAE1B,OAAO,CAAC,IAAI,CACV,2DAA2D,KAAK,CAAC,IAAI,SAAS,KAAK,CAAC,IAAI,YAAY,WAAW,EAAE,CAClH,CAAC;IACJ,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,IAAI,CAAC,mDAAmD,KAAK,CAAC,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC;IACrF,CAAC;IACD,OAAO,OAAO,CAAC,OAAO,EAAE,CAAC;AAC3B,CAAC"}
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.53.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",