@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.
- package/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -1
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +101 -58
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/history.d.ts +7 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +56 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +11 -73
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +11 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +47 -11
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +2 -0
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +11 -0
- package/dist/commands/run.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +66 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +6 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +13 -0
- package/dist/constants.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +116 -0
- package/dist/server/index.js.map +1 -1
- package/dist/types.d.ts +17 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/crontab.d.ts.map +1 -1
- package/dist/utils/crontab.js +4 -0
- package/dist/utils/crontab.js.map +1 -1
- package/dist/utils/execution-history.d.ts +48 -0
- package/dist/utils/execution-history.d.ts.map +1 -0
- package/dist/utils/execution-history.js +183 -0
- package/dist/utils/execution-history.js.map +1 -0
- package/dist/utils/roadmap-parser.d.ts +45 -0
- package/dist/utils/roadmap-parser.d.ts.map +1 -0
- package/dist/utils/roadmap-parser.js +136 -0
- package/dist/utils/roadmap-parser.js.map +1 -0
- package/dist/utils/roadmap-scanner.d.ts +60 -0
- package/dist/utils/roadmap-scanner.d.ts.map +1 -0
- package/dist/utils/roadmap-scanner.js +236 -0
- package/dist/utils/roadmap-scanner.js.map +1 -0
- package/dist/utils/roadmap-state.d.ts +83 -0
- package/dist/utils/roadmap-state.d.ts.map +1 -0
- package/dist/utils/roadmap-state.js +131 -0
- package/dist/utils/roadmap-state.js.map +1 -0
- package/dist/utils/status-data.d.ts.map +1 -1
- package/dist/utils/status-data.js +4 -3
- package/dist/utils/status-data.js.map +1 -1
- package/package.json +1 -1
- package/scripts/night-watch-cron.sh +65 -27
- package/scripts/night-watch-helpers.sh +54 -0
- package/web/dist/assets/index-DWgdrh9z.js +335 -0
- package/web/dist/index.html +1 -1
- 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
|