@jonit-dev/night-watch-cli 1.4.7 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/cli.js +3 -0
  2. package/dist/cli.js.map +1 -1
  3. package/dist/commands/doctor.d.ts +1 -1
  4. package/dist/commands/doctor.d.ts.map +1 -1
  5. package/dist/commands/doctor.js +101 -58
  6. package/dist/commands/doctor.js.map +1 -1
  7. package/dist/commands/history.d.ts +7 -0
  8. package/dist/commands/history.d.ts.map +1 -0
  9. package/dist/commands/history.js +56 -0
  10. package/dist/commands/history.js.map +1 -0
  11. package/dist/commands/init.d.ts.map +1 -1
  12. package/dist/commands/init.js +11 -73
  13. package/dist/commands/init.js.map +1 -1
  14. package/dist/commands/install.d.ts +11 -0
  15. package/dist/commands/install.d.ts.map +1 -1
  16. package/dist/commands/install.js +47 -11
  17. package/dist/commands/install.js.map +1 -1
  18. package/dist/commands/review.d.ts.map +1 -1
  19. package/dist/commands/review.js +2 -0
  20. package/dist/commands/review.js.map +1 -1
  21. package/dist/commands/run.d.ts.map +1 -1
  22. package/dist/commands/run.js +11 -0
  23. package/dist/commands/run.js.map +1 -1
  24. package/dist/config.d.ts.map +1 -1
  25. package/dist/config.js +66 -1
  26. package/dist/config.js.map +1 -1
  27. package/dist/constants.d.ts +6 -1
  28. package/dist/constants.d.ts.map +1 -1
  29. package/dist/constants.js +13 -0
  30. package/dist/constants.js.map +1 -1
  31. package/dist/server/index.d.ts.map +1 -1
  32. package/dist/server/index.js +116 -0
  33. package/dist/server/index.js.map +1 -1
  34. package/dist/types.d.ts +17 -0
  35. package/dist/types.d.ts.map +1 -1
  36. package/dist/utils/crontab.d.ts.map +1 -1
  37. package/dist/utils/crontab.js +4 -0
  38. package/dist/utils/crontab.js.map +1 -1
  39. package/dist/utils/execution-history.d.ts +48 -0
  40. package/dist/utils/execution-history.d.ts.map +1 -0
  41. package/dist/utils/execution-history.js +183 -0
  42. package/dist/utils/execution-history.js.map +1 -0
  43. package/dist/utils/roadmap-parser.d.ts +45 -0
  44. package/dist/utils/roadmap-parser.d.ts.map +1 -0
  45. package/dist/utils/roadmap-parser.js +136 -0
  46. package/dist/utils/roadmap-parser.js.map +1 -0
  47. package/dist/utils/roadmap-scanner.d.ts +60 -0
  48. package/dist/utils/roadmap-scanner.d.ts.map +1 -0
  49. package/dist/utils/roadmap-scanner.js +236 -0
  50. package/dist/utils/roadmap-scanner.js.map +1 -0
  51. package/dist/utils/roadmap-state.d.ts +83 -0
  52. package/dist/utils/roadmap-state.d.ts.map +1 -0
  53. package/dist/utils/roadmap-state.js +131 -0
  54. package/dist/utils/roadmap-state.js.map +1 -0
  55. package/dist/utils/status-data.d.ts.map +1 -1
  56. package/dist/utils/status-data.js +4 -3
  57. package/dist/utils/status-data.js.map +1 -1
  58. package/package.json +1 -1
  59. package/scripts/night-watch-cron.sh +65 -27
  60. package/scripts/night-watch-helpers.sh +54 -0
  61. package/web/dist/assets/index-DWgdrh9z.js +335 -0
  62. package/web/dist/index.html +1 -1
  63. package/web/dist/assets/index-CGNSV8kG.js +0 -325
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-parser.d.ts","sourceRoot":"","sources":["../../src/utils/roadmap-parser.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,oCAAoC;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,gEAAgE;IAChE,OAAO,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAGtD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,EAAE,CAuD5D;AA4DD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,YAAY,EAAE,CAEvE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,EAAE,CAAC,CAWpF"}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Roadmap Parser for Night Watch CLI
3
+ * Parses ROADMAP.md files into structured items
4
+ */
5
+ import * as crypto from "crypto";
6
+ /**
7
+ * Generate a hash for an item based on its title
8
+ * Uses first 8 characters of SHA-256 hash of lowercase trimmed title
9
+ */
10
+ export function generateItemHash(title) {
11
+ const normalizedTitle = title.toLowerCase().trim();
12
+ return crypto.createHash("sha256").update(normalizedTitle).digest("hex").slice(0, 8);
13
+ }
14
+ /**
15
+ * Parse ROADMAP.md content into structured items
16
+ *
17
+ * Supports two formats:
18
+ * 1. Checklist format: Lines matching `- [ ] Title` or `- [x] Title`
19
+ * with optional description on following indented lines
20
+ * 2. Heading format: `### Title` followed by body text (until next heading)
21
+ *
22
+ * @param content - The raw ROADMAP.md content
23
+ * @returns Array of parsed roadmap items
24
+ */
25
+ export function parseRoadmap(content) {
26
+ const items = [];
27
+ const lines = content.split("\n");
28
+ let currentSection = "General";
29
+ for (let i = 0; i < lines.length; i++) {
30
+ const line = lines[i];
31
+ // Track section headers (## headings)
32
+ const sectionMatch = line.match(/^##\s+(.+)$/);
33
+ if (sectionMatch) {
34
+ currentSection = sectionMatch[1].trim();
35
+ continue;
36
+ }
37
+ // Parse checklist items: - [ ] Title or - [x] Title
38
+ const checklistMatch = line.match(/^-\s+\[([ xX])\]\s+(.+)$/);
39
+ if (checklistMatch) {
40
+ const checked = checklistMatch[1].toLowerCase() === "x";
41
+ const title = checklistMatch[2].trim();
42
+ // Collect description from following indented lines
43
+ const description = collectDescription(lines, i + 1);
44
+ items.push({
45
+ hash: generateItemHash(title),
46
+ title,
47
+ description,
48
+ checked,
49
+ section: currentSection,
50
+ });
51
+ continue;
52
+ }
53
+ // Parse heading-based items: ### Title followed by body
54
+ const headingMatch = line.match(/^###\s+(.+)$/);
55
+ if (headingMatch) {
56
+ const title = headingMatch[1].trim();
57
+ // Collect description from following lines until next heading
58
+ const description = collectDescriptionUntilNextHeading(lines, i + 1);
59
+ items.push({
60
+ hash: generateItemHash(title),
61
+ title,
62
+ description,
63
+ checked: false, // Heading-based items are never checked
64
+ section: currentSection,
65
+ });
66
+ continue;
67
+ }
68
+ }
69
+ return items;
70
+ }
71
+ /**
72
+ * Collect description from indented lines following a checklist item
73
+ * Stops when reaching a non-indented line, another list item, or a heading
74
+ */
75
+ function collectDescription(lines, startIndex) {
76
+ const descriptionLines = [];
77
+ for (let i = startIndex; i < lines.length; i++) {
78
+ const line = lines[i];
79
+ // Stop at empty line
80
+ if (line.trim() === "") {
81
+ break;
82
+ }
83
+ // Stop at non-indented line (including headings, other list items)
84
+ if (!line.startsWith(" ") && !line.startsWith("\t")) {
85
+ break;
86
+ }
87
+ // Add the line, stripping leading indentation
88
+ descriptionLines.push(line.trim());
89
+ }
90
+ return descriptionLines.join("\n");
91
+ }
92
+ /**
93
+ * Collect description from lines following a ### heading
94
+ * Stops when reaching another heading (## or ###)
95
+ */
96
+ function collectDescriptionUntilNextHeading(lines, startIndex) {
97
+ const descriptionLines = [];
98
+ for (let i = startIndex; i < lines.length; i++) {
99
+ const line = lines[i];
100
+ // Stop at any heading
101
+ if (line.match(/^#{1,3}\s+/)) {
102
+ break;
103
+ }
104
+ // Stop at empty line followed by another heading
105
+ if (line.trim() === "") {
106
+ // Look ahead for heading
107
+ if (i + 1 < lines.length && lines[i + 1].match(/^#{1,3}\s+/)) {
108
+ break;
109
+ }
110
+ }
111
+ descriptionLines.push(line);
112
+ }
113
+ // Trim trailing empty lines and join
114
+ const result = descriptionLines.join("\n").trim();
115
+ return result;
116
+ }
117
+ /**
118
+ * Filter roadmap items to only unchecked items (pending work)
119
+ */
120
+ export function getUncheckedItems(items) {
121
+ return items.filter((item) => !item.checked);
122
+ }
123
+ /**
124
+ * Group roadmap items by section
125
+ */
126
+ export function groupBySection(items) {
127
+ const groups = {};
128
+ for (const item of items) {
129
+ if (!groups[item.section]) {
130
+ groups[item.section] = [];
131
+ }
132
+ groups[item.section].push(item);
133
+ }
134
+ return groups;
135
+ }
136
+ //# sourceMappingURL=roadmap-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-parser.js","sourceRoot":"","sources":["../../src/utils/roadmap-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Roadmap Scanner for Night Watch CLI
3
+ * Scans ROADMAP.md files and generates PRD skeleton files
4
+ */
5
+ import { INightWatchConfig } from "../types.js";
6
+ import { IRoadmapItem } from "./roadmap-parser.js";
7
+ /**
8
+ * Status of the roadmap scanner
9
+ */
10
+ export interface IRoadmapStatus {
11
+ /** Whether ROADMAP.md file was found */
12
+ found: boolean;
13
+ /** Whether the scanner is enabled in config */
14
+ enabled: boolean;
15
+ /** Total number of items in roadmap */
16
+ totalItems: number;
17
+ /** Number of items that have been processed */
18
+ processedItems: number;
19
+ /** Number of items pending processing */
20
+ pendingItems: number;
21
+ /** Current status of the scanner */
22
+ status: "idle" | "scanning" | "complete" | "disabled" | "no-roadmap";
23
+ /** All roadmap items with processing status */
24
+ items: Array<IRoadmapItem & {
25
+ processed: boolean;
26
+ prdFile?: string;
27
+ }>;
28
+ }
29
+ /**
30
+ * Result of a roadmap scan operation
31
+ */
32
+ export interface IScanResult {
33
+ /** List of PRD files created */
34
+ created: string[];
35
+ /** List of items skipped (already processed or checked) */
36
+ skipped: string[];
37
+ /** List of errors encountered */
38
+ errors: string[];
39
+ }
40
+ /**
41
+ * Get the current status of the roadmap scanner
42
+ *
43
+ * @param projectDir - The project directory
44
+ * @param config - The Night Watch configuration
45
+ * @returns The roadmap scanner status
46
+ */
47
+ export declare function getRoadmapStatus(projectDir: string, config: INightWatchConfig): IRoadmapStatus;
48
+ /**
49
+ * Scan the roadmap and create PRD files for unprocessed items
50
+ *
51
+ * @param projectDir - The project directory
52
+ * @param config - The Night Watch configuration
53
+ * @returns The scan result with created, skipped, and error lists
54
+ */
55
+ export declare function scanRoadmap(projectDir: string, config: INightWatchConfig): IScanResult;
56
+ /**
57
+ * Check if there are new (unprocessed) items in the roadmap
58
+ */
59
+ export declare function hasNewItems(projectDir: string, config: INightWatchConfig): boolean;
60
+ //# sourceMappingURL=roadmap-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-scanner.d.ts","sourceRoot":"","sources":["../../src/utils/roadmap-scanner.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAGhD,OAAO,EAAE,YAAY,EAAgB,MAAM,qBAAqB,CAAC;AASjE;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,wCAAwC;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,OAAO,EAAE,OAAO,CAAC;IACjB,uCAAuC;IACvC,UAAU,EAAE,MAAM,CAAC;IACnB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,CAAC;IACvB,yCAAyC;IACzC,YAAY,EAAE,MAAM,CAAC;IACrB,oCAAoC;IACpC,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,GAAG,YAAY,CAAC;IACrE,+CAA+C;IAC/C,KAAK,EAAE,KAAK,CAAC,YAAY,GAAG;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CACvE;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,gCAAgC;IAChC,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,iCAAiC;IACjC,MAAM,EAAE,MAAM,EAAE,CAAC;CAClB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACxB,cAAc,CA+EhB;AAED;;;;;;GAMG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACxB,WAAW,CAsFb;AAED;;GAEG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,iBAAiB,GACxB,OAAO,CAGT"}
@@ -0,0 +1,236 @@
1
+ /**
2
+ * Roadmap Scanner for Night Watch CLI
3
+ * Scans ROADMAP.md files and generates PRD skeleton files
4
+ */
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ import { getNextPrdNumber, slugify } from "../commands/prd.js";
8
+ import { renderPrdTemplate } from "../templates/prd-template.js";
9
+ import { parseRoadmap } from "./roadmap-parser.js";
10
+ import { isItemProcessed, loadRoadmapState, markItemProcessed, saveRoadmapState, } from "./roadmap-state.js";
11
+ /**
12
+ * Get the current status of the roadmap scanner
13
+ *
14
+ * @param projectDir - The project directory
15
+ * @param config - The Night Watch configuration
16
+ * @returns The roadmap scanner status
17
+ */
18
+ export function getRoadmapStatus(projectDir, config) {
19
+ const roadmapPath = path.join(projectDir, config.roadmapScanner.roadmapPath);
20
+ // Check if enabled
21
+ if (!config.roadmapScanner.enabled) {
22
+ return {
23
+ found: false,
24
+ enabled: false,
25
+ totalItems: 0,
26
+ processedItems: 0,
27
+ pendingItems: 0,
28
+ status: "disabled",
29
+ items: [],
30
+ };
31
+ }
32
+ // Check if roadmap file exists
33
+ if (!fs.existsSync(roadmapPath)) {
34
+ return {
35
+ found: false,
36
+ enabled: true,
37
+ totalItems: 0,
38
+ processedItems: 0,
39
+ pendingItems: 0,
40
+ status: "no-roadmap",
41
+ items: [],
42
+ };
43
+ }
44
+ // Parse roadmap
45
+ const content = fs.readFileSync(roadmapPath, "utf-8");
46
+ const items = parseRoadmap(content);
47
+ // Load state
48
+ const prdDir = path.join(projectDir, config.prdDir);
49
+ const state = loadRoadmapState(prdDir);
50
+ // Scan existing PRD files for title-based duplicate detection
51
+ const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
52
+ // Build status items
53
+ const statusItems = items.map((item) => {
54
+ const processed = isItemProcessed(state, item.hash);
55
+ const stateItem = state.items[item.hash];
56
+ // Also check for duplicates by title match
57
+ const itemSlug = slugify(item.title);
58
+ const isDuplicateByTitle = existingPrdSlugs.has(itemSlug) && !processed;
59
+ return {
60
+ ...item,
61
+ processed: processed || isDuplicateByTitle,
62
+ prdFile: stateItem?.prdFile,
63
+ };
64
+ });
65
+ // Count processed and pending
66
+ const processedItems = statusItems.filter((item) => item.processed).length;
67
+ const pendingItems = statusItems.filter((item) => !item.processed && !item.checked).length;
68
+ // Determine status
69
+ let status;
70
+ if (pendingItems === 0 && statusItems.length > 0) {
71
+ status = "complete";
72
+ }
73
+ else {
74
+ status = "idle";
75
+ }
76
+ return {
77
+ found: true,
78
+ enabled: true,
79
+ totalItems: items.length,
80
+ processedItems,
81
+ pendingItems,
82
+ status,
83
+ items: statusItems,
84
+ };
85
+ }
86
+ /**
87
+ * Scan the roadmap and create PRD files for unprocessed items
88
+ *
89
+ * @param projectDir - The project directory
90
+ * @param config - The Night Watch configuration
91
+ * @returns The scan result with created, skipped, and error lists
92
+ */
93
+ export function scanRoadmap(projectDir, config) {
94
+ const result = {
95
+ created: [],
96
+ skipped: [],
97
+ errors: [],
98
+ };
99
+ // Check if enabled
100
+ if (!config.roadmapScanner.enabled) {
101
+ return result;
102
+ }
103
+ const roadmapPath = path.join(projectDir, config.roadmapScanner.roadmapPath);
104
+ // Check if roadmap file exists
105
+ if (!fs.existsSync(roadmapPath)) {
106
+ return result;
107
+ }
108
+ // Parse roadmap
109
+ const content = fs.readFileSync(roadmapPath, "utf-8");
110
+ const items = parseRoadmap(content);
111
+ if (items.length === 0) {
112
+ return result;
113
+ }
114
+ // Setup PRD directory
115
+ const prdDir = path.join(projectDir, config.prdDir);
116
+ if (!fs.existsSync(prdDir)) {
117
+ fs.mkdirSync(prdDir, { recursive: true });
118
+ }
119
+ // Load state
120
+ let state = loadRoadmapState(prdDir);
121
+ // Scan existing PRD files for title-based duplicate detection
122
+ const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
123
+ // Process each item
124
+ for (const item of items) {
125
+ // Skip checked items (completed in roadmap)
126
+ if (item.checked) {
127
+ result.skipped.push(`${item.title} (checked)`);
128
+ continue;
129
+ }
130
+ // Skip already processed items
131
+ if (isItemProcessed(state, item.hash)) {
132
+ result.skipped.push(`${item.title} (processed)`);
133
+ continue;
134
+ }
135
+ // Skip items that have a PRD with matching title
136
+ const itemSlug = slugify(item.title);
137
+ if (existingPrdSlugs.has(itemSlug)) {
138
+ result.skipped.push(`${item.title} (duplicate by title)`);
139
+ continue;
140
+ }
141
+ try {
142
+ // Generate PRD file
143
+ const prdFile = createPrdFromItem(prdDir, item);
144
+ // Update state
145
+ const stateItem = {
146
+ title: item.title,
147
+ prdFile,
148
+ createdAt: new Date().toISOString(),
149
+ };
150
+ state = markItemProcessed(state, item.hash, stateItem);
151
+ // Add to existing slugs to prevent duplicates in same scan
152
+ existingPrdSlugs.add(itemSlug);
153
+ result.created.push(prdFile);
154
+ }
155
+ catch (error) {
156
+ const errorMessage = error instanceof Error ? error.message : String(error);
157
+ result.errors.push(`${item.title}: ${errorMessage}`);
158
+ }
159
+ }
160
+ // Save state
161
+ saveRoadmapState(prdDir, state);
162
+ return result;
163
+ }
164
+ /**
165
+ * Check if there are new (unprocessed) items in the roadmap
166
+ */
167
+ export function hasNewItems(projectDir, config) {
168
+ const status = getRoadmapStatus(projectDir, config);
169
+ return status.pendingItems > 0;
170
+ }
171
+ /**
172
+ * Scan existing PRD files and extract their slugs for duplicate detection
173
+ */
174
+ function scanExistingPrdSlugs(prdDir) {
175
+ const slugs = new Set();
176
+ if (!fs.existsSync(prdDir)) {
177
+ return slugs;
178
+ }
179
+ const files = fs.readdirSync(prdDir);
180
+ for (const file of files) {
181
+ // Skip non-markdown files and special files
182
+ if (!file.endsWith(".md") || file === "NIGHT-WATCH-SUMMARY.md") {
183
+ continue;
184
+ }
185
+ // Extract slug from filename (e.g., "01-feature-name.md" -> "feature-name")
186
+ const match = file.match(/^\d+-(.+)\.md$/);
187
+ if (match) {
188
+ slugs.add(match[1]);
189
+ }
190
+ else {
191
+ // Handle files without number prefix
192
+ const slugMatch = file.match(/^(.+)\.md$/);
193
+ if (slugMatch) {
194
+ slugs.add(slugMatch[1]);
195
+ }
196
+ }
197
+ }
198
+ return slugs;
199
+ }
200
+ /**
201
+ * Create a PRD file from a roadmap item
202
+ *
203
+ * @param prdDir - The PRD directory
204
+ * @param item - The roadmap item
205
+ * @returns The filename of the created PRD
206
+ */
207
+ function createPrdFromItem(prdDir, item) {
208
+ // Get next PRD number
209
+ const nextNum = getNextPrdNumber(prdDir);
210
+ const padded = String(nextNum).padStart(2, "0");
211
+ // Generate filename
212
+ const slug = slugify(item.title);
213
+ const filename = `${padded}-${slug}.md`;
214
+ const filePath = path.join(prdDir, filename);
215
+ // Render PRD template
216
+ const prdContent = renderPrdTemplate({
217
+ title: item.title,
218
+ dependsOn: [],
219
+ complexityScore: 5,
220
+ complexityLevel: "MEDIUM",
221
+ complexityBreakdown: [],
222
+ phaseCount: 3,
223
+ });
224
+ // Prepend roadmap context comment
225
+ const roadmapContext = `<!-- Roadmap Context:
226
+ Section: ${item.section}
227
+ Description: ${item.description}
228
+ -->
229
+
230
+ `;
231
+ const fullContent = roadmapContext + prdContent;
232
+ // Write file
233
+ fs.writeFileSync(filePath, fullContent, "utf-8");
234
+ return filename;
235
+ }
236
+ //# sourceMappingURL=roadmap-scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-scanner.js","sourceRoot":"","sources":["../../src/utils/roadmap-scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Roadmap State Manager for Night Watch CLI
3
+ * Manages .roadmap-state.json for tracking processed items
4
+ */
5
+ /**
6
+ * Represents the state of a single processed roadmap item
7
+ */
8
+ export interface IRoadmapStateItem {
9
+ /** Original title from roadmap */
10
+ title: string;
11
+ /** Path to the generated PRD file */
12
+ prdFile: string;
13
+ /** ISO timestamp when item was processed */
14
+ createdAt: string;
15
+ }
16
+ /**
17
+ * Represents the full roadmap state persisted to .roadmap-state.json
18
+ */
19
+ export interface IRoadmapState {
20
+ /** State file format version */
21
+ version: number;
22
+ /** ISO timestamp of last scan */
23
+ lastScan: string;
24
+ /** Map of item hash to state item */
25
+ items: Record<string, IRoadmapStateItem>;
26
+ }
27
+ /**
28
+ * Get the path to the roadmap state file
29
+ */
30
+ export declare function getStateFilePath(prdDir: string): string;
31
+ /**
32
+ * Load the roadmap state from disk
33
+ * Returns an empty state if the file does not exist or is invalid
34
+ *
35
+ * @param prdDir - Directory containing PRD files (where state file lives)
36
+ * @returns The loaded or empty roadmap state
37
+ */
38
+ export declare function loadRoadmapState(prdDir: string): IRoadmapState;
39
+ /**
40
+ * Save the roadmap state to disk
41
+ *
42
+ * @param prdDir - Directory containing PRD files (where state file lives)
43
+ * @param state - The state to save
44
+ */
45
+ export declare function saveRoadmapState(prdDir: string, state: IRoadmapState): void;
46
+ /**
47
+ * Create an empty roadmap state
48
+ */
49
+ export declare function createEmptyState(): IRoadmapState;
50
+ /**
51
+ * Check if an item has already been processed
52
+ *
53
+ * @param state - The roadmap state
54
+ * @param hash - The item hash to check
55
+ * @returns True if the item has been processed
56
+ */
57
+ export declare function isItemProcessed(state: IRoadmapState, hash: string): boolean;
58
+ /**
59
+ * Mark an item as processed in the state
60
+ *
61
+ * @param state - The roadmap state (will be mutated)
62
+ * @param hash - The item hash
63
+ * @param item - The state item data
64
+ * @returns The updated state
65
+ */
66
+ export declare function markItemProcessed(state: IRoadmapState, hash: string, item: IRoadmapStateItem): IRoadmapState;
67
+ /**
68
+ * Remove an item from the state (e.g., if PRD was deleted)
69
+ *
70
+ * @param state - The roadmap state (will be mutated)
71
+ * @param hash - The item hash to remove
72
+ * @returns True if the item was found and removed
73
+ */
74
+ export declare function unmarkItemProcessed(state: IRoadmapState, hash: string): boolean;
75
+ /**
76
+ * Get all processed item hashes
77
+ */
78
+ export declare function getProcessedHashes(state: IRoadmapState): string[];
79
+ /**
80
+ * Get state item by hash
81
+ */
82
+ export declare function getStateItem(state: IRoadmapState, hash: string): IRoadmapStateItem | undefined;
83
+ //# sourceMappingURL=roadmap-state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roadmap-state.d.ts","sourceRoot":"","sources":["../../src/utils/roadmap-state.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,kCAAkC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,iCAAiC;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,qCAAqC;IACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CAC1C;AAQD;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,aAAa,CAmC9D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CAc3E;AAED;;GAEG;AACH,wBAAgB,gBAAgB,IAAI,aAAa,CAMhD;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAE3E;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,iBAAiB,GACtB,aAAa,CAGf;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAM/E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,EAAE,CAEjE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,aAAa,EACpB,IAAI,EAAE,MAAM,GACX,iBAAiB,GAAG,SAAS,CAE/B"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * Roadmap State Manager for Night Watch CLI
3
+ * Manages .roadmap-state.json for tracking processed items
4
+ */
5
+ import * as fs from "fs";
6
+ import * as path from "path";
7
+ /** Current version of the state file format */
8
+ const STATE_VERSION = 1;
9
+ /** Name of the state file */
10
+ const STATE_FILE_NAME = ".roadmap-state.json";
11
+ /**
12
+ * Get the path to the roadmap state file
13
+ */
14
+ export function getStateFilePath(prdDir) {
15
+ return path.join(prdDir, STATE_FILE_NAME);
16
+ }
17
+ /**
18
+ * Load the roadmap state from disk
19
+ * Returns an empty state if the file does not exist or is invalid
20
+ *
21
+ * @param prdDir - Directory containing PRD files (where state file lives)
22
+ * @returns The loaded or empty roadmap state
23
+ */
24
+ export function loadRoadmapState(prdDir) {
25
+ const statePath = getStateFilePath(prdDir);
26
+ if (!fs.existsSync(statePath)) {
27
+ return createEmptyState();
28
+ }
29
+ try {
30
+ const content = fs.readFileSync(statePath, "utf-8");
31
+ const parsed = JSON.parse(content);
32
+ // Validate structure
33
+ if (typeof parsed !== "object" || parsed === null) {
34
+ return createEmptyState();
35
+ }
36
+ // Ensure version and items exist
37
+ if (typeof parsed.version !== "number" || typeof parsed.items !== "object") {
38
+ return createEmptyState();
39
+ }
40
+ // Validate lastScan is a string if present
41
+ if (parsed.lastScan !== undefined && typeof parsed.lastScan !== "string") {
42
+ parsed.lastScan = new Date().toISOString();
43
+ }
44
+ return {
45
+ version: parsed.version,
46
+ lastScan: parsed.lastScan || "",
47
+ items: parsed.items || {},
48
+ };
49
+ }
50
+ catch {
51
+ // Invalid JSON or other error
52
+ return createEmptyState();
53
+ }
54
+ }
55
+ /**
56
+ * Save the roadmap state to disk
57
+ *
58
+ * @param prdDir - Directory containing PRD files (where state file lives)
59
+ * @param state - The state to save
60
+ */
61
+ export function saveRoadmapState(prdDir, state) {
62
+ const statePath = getStateFilePath(prdDir);
63
+ // Ensure directory exists
64
+ const dir = path.dirname(statePath);
65
+ if (!fs.existsSync(dir)) {
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ }
68
+ // Update lastScan timestamp
69
+ state.lastScan = new Date().toISOString();
70
+ // Write with pretty formatting
71
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
72
+ }
73
+ /**
74
+ * Create an empty roadmap state
75
+ */
76
+ export function createEmptyState() {
77
+ return {
78
+ version: STATE_VERSION,
79
+ lastScan: "",
80
+ items: {},
81
+ };
82
+ }
83
+ /**
84
+ * Check if an item has already been processed
85
+ *
86
+ * @param state - The roadmap state
87
+ * @param hash - The item hash to check
88
+ * @returns True if the item has been processed
89
+ */
90
+ export function isItemProcessed(state, hash) {
91
+ return hash in state.items;
92
+ }
93
+ /**
94
+ * Mark an item as processed in the state
95
+ *
96
+ * @param state - The roadmap state (will be mutated)
97
+ * @param hash - The item hash
98
+ * @param item - The state item data
99
+ * @returns The updated state
100
+ */
101
+ export function markItemProcessed(state, hash, item) {
102
+ state.items[hash] = item;
103
+ return state;
104
+ }
105
+ /**
106
+ * Remove an item from the state (e.g., if PRD was deleted)
107
+ *
108
+ * @param state - The roadmap state (will be mutated)
109
+ * @param hash - The item hash to remove
110
+ * @returns True if the item was found and removed
111
+ */
112
+ export function unmarkItemProcessed(state, hash) {
113
+ if (hash in state.items) {
114
+ delete state.items[hash];
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+ /**
120
+ * Get all processed item hashes
121
+ */
122
+ export function getProcessedHashes(state) {
123
+ return Object.keys(state.items);
124
+ }
125
+ /**
126
+ * Get state item by hash
127
+ */
128
+ export function getStateItem(state, hash) {
129
+ return state.items[hash];
130
+ }
131
+ //# sourceMappingURL=roadmap-state.js.map