@harness-engineering/cli 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,70 @@
1
+ // src/commands/validate-cross-check.ts
2
+ import * as fs from "fs";
3
+ import * as path from "path";
4
+ import { execSync } from "child_process";
5
+ import { Ok } from "@harness-engineering/core";
6
+ function findFiles(dir, ext) {
7
+ if (!fs.existsSync(dir)) return [];
8
+ return fs.readdirSync(dir).filter((f) => f.endsWith(ext)).map((f) => path.join(dir, f));
9
+ }
10
+ function extractPlannedFiles(planContent) {
11
+ const files = [];
12
+ const regex = /- (?:Create|Modify):\s*`([^`]+)`/g;
13
+ let match;
14
+ while ((match = regex.exec(planContent)) !== null) {
15
+ if (match[1]) files.push(match[1]);
16
+ }
17
+ return files;
18
+ }
19
+ function getFileModTime(filePath, projectPath) {
20
+ try {
21
+ const output = execSync(`git log -1 --format=%aI -- "${filePath}"`, {
22
+ cwd: projectPath,
23
+ encoding: "utf-8",
24
+ stdio: ["pipe", "pipe", "pipe"]
25
+ }).trim();
26
+ return output ? new Date(output) : null;
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+ async function runCrossCheck(options) {
32
+ const result = {
33
+ specToPlan: [],
34
+ planToImpl: [],
35
+ staleness: [],
36
+ warnings: 0
37
+ };
38
+ const planFiles = findFiles(options.plansDir, ".md");
39
+ for (const planFile of planFiles) {
40
+ const content = fs.readFileSync(planFile, "utf-8");
41
+ const plannedFiles = extractPlannedFiles(content);
42
+ const planName = path.basename(planFile);
43
+ for (const file of plannedFiles) {
44
+ const fullPath = path.join(options.projectPath, file);
45
+ if (!fs.existsSync(fullPath)) {
46
+ result.planToImpl.push(`${planName}: planned file not found: ${file}`);
47
+ result.warnings++;
48
+ }
49
+ }
50
+ const planModTime = getFileModTime(planFile, options.projectPath);
51
+ if (planModTime) {
52
+ for (const file of plannedFiles) {
53
+ const fullPath = path.join(options.projectPath, file);
54
+ if (fs.existsSync(fullPath)) {
55
+ const implModTime = getFileModTime(fullPath, options.projectPath);
56
+ if (implModTime && implModTime > planModTime) {
57
+ result.staleness.push(`${planName}: implementation newer than plan (${file})`);
58
+ result.warnings++;
59
+ break;
60
+ }
61
+ }
62
+ }
63
+ }
64
+ }
65
+ return Ok(result);
66
+ }
67
+
68
+ export {
69
+ runCrossCheck
70
+ };