@jonit-dev/night-watch-cli 1.1.4 → 1.1.5

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 (50) hide show
  1. package/README.md +48 -426
  2. package/dist/cli.js +6 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/doctor.d.ts +16 -0
  5. package/dist/commands/doctor.d.ts.map +1 -0
  6. package/dist/commands/doctor.js +155 -0
  7. package/dist/commands/doctor.js.map +1 -0
  8. package/dist/commands/init.d.ts.map +1 -1
  9. package/dist/commands/init.js +15 -9
  10. package/dist/commands/init.js.map +1 -1
  11. package/dist/commands/prd.d.ts +24 -0
  12. package/dist/commands/prd.d.ts.map +1 -0
  13. package/dist/commands/prd.js +283 -0
  14. package/dist/commands/prd.js.map +1 -0
  15. package/dist/commands/review.d.ts.map +1 -1
  16. package/dist/commands/review.js +24 -0
  17. package/dist/commands/review.js.map +1 -1
  18. package/dist/commands/run.d.ts +19 -0
  19. package/dist/commands/run.d.ts.map +1 -1
  20. package/dist/commands/run.js +55 -6
  21. package/dist/commands/run.js.map +1 -1
  22. package/dist/commands/status.d.ts.map +1 -1
  23. package/dist/commands/status.js +27 -6
  24. package/dist/commands/status.js.map +1 -1
  25. package/dist/config.d.ts.map +1 -1
  26. package/dist/config.js +40 -1
  27. package/dist/config.js.map +1 -1
  28. package/dist/constants.d.ts +3 -1
  29. package/dist/constants.d.ts.map +1 -1
  30. package/dist/constants.js +3 -0
  31. package/dist/constants.js.map +1 -1
  32. package/dist/templates/prd-template.d.ts +11 -0
  33. package/dist/templates/prd-template.d.ts.map +1 -0
  34. package/dist/templates/prd-template.js +166 -0
  35. package/dist/templates/prd-template.js.map +1 -0
  36. package/dist/types.d.ts +14 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/utils/github.d.ts +30 -0
  39. package/dist/utils/github.d.ts.map +1 -0
  40. package/dist/utils/github.js +104 -0
  41. package/dist/utils/github.js.map +1 -0
  42. package/dist/utils/notify.d.ts +63 -0
  43. package/dist/utils/notify.d.ts.map +1 -0
  44. package/dist/utils/notify.js +237 -0
  45. package/dist/utils/notify.js.map +1 -0
  46. package/package.json +4 -4
  47. package/scripts/night-watch-cron.sh +8 -1
  48. package/scripts/night-watch-helpers.sh +51 -0
  49. package/scripts/test-helpers.bats +77 -0
  50. package/templates/prd.md +26 -0
@@ -0,0 +1,166 @@
1
+ export const PRD_TEMPLATE = `# PRD: {{TITLE}}
2
+
3
+ {{DEPENDS_ON}}
4
+
5
+ **Complexity: {{COMPLEXITY_SCORE}} → {{COMPLEXITY_LEVEL}} mode**
6
+ {{COMPLEXITY_BREAKDOWN}}
7
+
8
+ \`\`\`
9
+ COMPLEXITY SCORE (sum all that apply):
10
+ +1 Touches 1-5 files
11
+ +2 Touches 6-10 files
12
+ +3 Touches 10+ files
13
+ +2 New system/module from scratch
14
+ +2 Complex state logic / concurrency
15
+ +2 Multi-package changes
16
+ +1 Database schema changes
17
+ +1 External API integration
18
+
19
+ | Score | Level | Template Mode |
20
+ | ----- | ------ | ----------------------------------------------- |
21
+ | 1-3 | LOW | Minimal (skip sections marked with MEDIUM/HIGH) |
22
+ | 4-6 | MEDIUM | Standard (all sections) |
23
+ | 7+ | HIGH | Full + mandatory checkpoints every phase |
24
+ \`\`\`
25
+
26
+ ---
27
+
28
+ ## 1. Context
29
+
30
+ **Problem:** <!-- 1-2 sentences describing the issue being solved -->
31
+
32
+ **Files Analyzed:**
33
+ <!-- List all files you've inspected before planning -->
34
+
35
+ **Current Behavior:**
36
+ <!-- 3-5 bullets describing current state -->
37
+
38
+ ### Integration Points Checklist
39
+
40
+ **How will this feature be reached?**
41
+ - [ ] Entry point identified: <!-- e.g., route, event, cron, CLI command -->
42
+ - [ ] Caller file identified: <!-- file that will invoke this new code -->
43
+ - [ ] Registration/wiring needed: <!-- e.g., add route to router, register handler, add menu item -->
44
+
45
+ **Is this user-facing?**
46
+ - [ ] YES → UI components required (list them)
47
+ - [ ] NO → Internal/background feature (explain how it's triggered)
48
+
49
+ **Full user flow:**
50
+ 1. User does: <!-- action -->
51
+ 2. Triggers: <!-- what code path -->
52
+ 3. Reaches new feature via: <!-- specific connection point -->
53
+ 4. Result displayed in: <!-- where user sees outcome -->
54
+
55
+ ---
56
+
57
+ ## 2. Solution
58
+
59
+ **Approach:**
60
+ <!-- 3-5 bullets explaining the chosen solution -->
61
+
62
+ **Architecture Diagram** <!-- (MEDIUM/HIGH complexity) -->:
63
+
64
+ \`\`\`mermaid
65
+ flowchart LR
66
+ A[Component A] --> B[Component B] --> C[Component C]
67
+ \`\`\`
68
+
69
+ **Key Decisions:**
70
+ <!-- Library/framework choices, error-handling strategy, reused utilities -->
71
+
72
+ **Data Changes:** <!-- New schemas/migrations, or "None" -->
73
+
74
+ ---
75
+
76
+ ## 3. Sequence Flow <!-- (MEDIUM/HIGH complexity) -->
77
+
78
+ \`\`\`mermaid
79
+ sequenceDiagram
80
+ participant A as Component A
81
+ participant B as Component B
82
+ A->>B: methodName(args)
83
+ alt Error case
84
+ B-->>A: ErrorType
85
+ else Success
86
+ B-->>A: Response
87
+ end
88
+ \`\`\`
89
+
90
+ ---
91
+
92
+ ## 4. Execution Phases
93
+
94
+ **CRITICAL RULES:**
95
+ 1. Each phase = ONE user-testable vertical slice
96
+ 2. Max 5 files per phase (split if larger)
97
+ 3. Each phase MUST include concrete tests
98
+ 4. Checkpoint after each phase (automated ALWAYS required)
99
+
100
+ {{PHASES}}
101
+
102
+ ---
103
+
104
+ ## 5. Acceptance Criteria
105
+
106
+ - [ ] All phases complete
107
+ - [ ] All specified tests pass
108
+ - [ ] Verification commands pass
109
+ - [ ] All automated checkpoint reviews passed
110
+ - [ ] Feature is reachable (entry point connected, not orphaned code)
111
+ - [ ] <!-- additional criterion -->
112
+ - [ ] <!-- additional criterion -->
113
+ `;
114
+ function renderDependsOn(deps) {
115
+ if (deps.length === 0) {
116
+ return "";
117
+ }
118
+ const formatted = deps.map((d) => `\`${d}\``).join(", ");
119
+ return `**Depends on:** ${formatted}`;
120
+ }
121
+ function renderComplexityBreakdown(items) {
122
+ if (items.length === 0) {
123
+ return "";
124
+ }
125
+ return items.map((item) => `- ${item}`).join("\n");
126
+ }
127
+ function renderPhases(count) {
128
+ const phases = [];
129
+ for (let i = 1; i <= count; i++) {
130
+ phases.push(`### Phase ${i}: [Name] — [User-visible outcome in 1 sentence]
131
+
132
+ **Files (max 5):**
133
+ - \`src/path/file.ts\` — what changes
134
+
135
+ **Implementation:**
136
+ - [ ] Step 1
137
+ - [ ] Step 2
138
+
139
+ **Tests Required:**
140
+ | Test File | Test Name | Assertion |
141
+ |-----------|-----------|-----------|
142
+ | \`src/__tests__/feature.test.ts\` | \`should do X when Y\` | \`expect(result).toBe(Z)\` |
143
+
144
+ **Verification Plan:**
145
+ 1. **Unit Tests:** File and test names
146
+ 2. **Integration Test:** (if applicable)
147
+ 3. **User Verification:**
148
+ - Action: [what to do]
149
+ - Expected: [what should happen]
150
+
151
+ **Checkpoint:** Run automated review after this phase completes.`);
152
+ }
153
+ return phases.join("\n\n---\n\n");
154
+ }
155
+ export function renderPrdTemplate(vars, customTemplate) {
156
+ const template = customTemplate ?? PRD_TEMPLATE;
157
+ let result = template;
158
+ result = result.replace("{{TITLE}}", vars.title);
159
+ result = result.replace("{{DEPENDS_ON}}", renderDependsOn(vars.dependsOn));
160
+ result = result.replace("{{COMPLEXITY_SCORE}}", String(vars.complexityScore));
161
+ result = result.replace("{{COMPLEXITY_LEVEL}}", vars.complexityLevel);
162
+ result = result.replace("{{COMPLEXITY_BREAKDOWN}}", renderComplexityBreakdown(vars.complexityBreakdown));
163
+ result = result.replace("{{PHASES}}", renderPhases(vars.phaseCount));
164
+ return result;
165
+ }
166
+ //# sourceMappingURL=prd-template.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prd-template.js","sourceRoot":"","sources":["../../src/templates/prd-template.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,YAAY,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAgH3B,CAAC;AAWF,SAAS,eAAe,CAAC,IAAc;IACrC,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACzD,OAAO,mBAAmB,SAAS,EAAE,CAAC;AACxC,CAAC;AAED,SAAS,yBAAyB,CAAC,KAAe;IAChD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,EAAE,CAAC;IACZ,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACrD,CAAC;AAED,SAAS,YAAY,CAAC,KAAa;IACjC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;QAChC,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC;;;;;;;;;;;;;;;;;;;;;iEAqBmC,CAAC,CAAC;IACjE,CAAC;IACD,OAAO,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,IAAqB,EACrB,cAAuB;IAEvB,MAAM,QAAQ,GAAG,cAAc,IAAI,YAAY,CAAC;IAEhD,IAAI,MAAM,GAAG,QAAQ,CAAC;IACtB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACjD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IAC3E,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,sBAAsB,EACtB,MAAM,CAAC,IAAI,CAAC,eAAe,CAAC,CAC7B,CAAC;IACF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,sBAAsB,EAAE,IAAI,CAAC,eAAe,CAAC,CAAC;IACtE,MAAM,GAAG,MAAM,CAAC,OAAO,CACrB,0BAA0B,EAC1B,yBAAyB,CAAC,IAAI,CAAC,mBAAmB,CAAC,CACpD,CAAC;IACF,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC;IAErE,OAAO,MAAM,CAAC;AAChB,CAAC"}
package/dist/types.d.ts CHANGED
@@ -35,5 +35,19 @@ export interface INightWatchConfig {
35
35
  reviewerEnabled: boolean;
36
36
  /** Extra environment variables to pass to the provider CLI (e.g. API keys, base URLs) */
37
37
  providerEnv: Record<string, string>;
38
+ /** Notification webhook configuration */
39
+ notifications: NotificationConfig;
40
+ }
41
+ export type WebhookType = "slack" | "discord" | "telegram";
42
+ export type NotificationEvent = "run_succeeded" | "run_failed" | "run_timeout" | "review_completed";
43
+ export interface WebhookConfig {
44
+ type: WebhookType;
45
+ url?: string;
46
+ botToken?: string;
47
+ chatId?: string;
48
+ events: NotificationEvent[];
49
+ }
50
+ export interface NotificationConfig {
51
+ webhooks: WebhookConfig[];
38
52
  }
39
53
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAGhC,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;IAEtB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IAEf,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IAEnB,iDAAiD;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IAErB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IAEvB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IAInB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IAErB,oCAAoC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IAIzB,uCAAuC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IAEnB,sCAAsC;IACtC,eAAe,EAAE,OAAO,CAAC;IAEzB,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACrC"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE1C;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAGhC,qFAAqF;IACrF,aAAa,EAAE,MAAM,CAAC;IAEtB,gEAAgE;IAChE,MAAM,EAAE,MAAM,CAAC;IAEf,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IAEnB,iDAAiD;IACjD,kBAAkB,EAAE,MAAM,CAAC;IAE3B,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IAErB,+CAA+C;IAC/C,cAAc,EAAE,MAAM,EAAE,CAAC;IAEzB,gEAAgE;IAChE,cAAc,EAAE,MAAM,CAAC;IAEvB,qDAAqD;IACrD,UAAU,EAAE,MAAM,CAAC;IAInB,sCAAsC;IACtC,YAAY,EAAE,MAAM,CAAC;IAErB,oCAAoC;IACpC,gBAAgB,EAAE,MAAM,CAAC;IAIzB,uCAAuC;IACvC,QAAQ,EAAE,QAAQ,CAAC;IAEnB,sCAAsC;IACtC,eAAe,EAAE,OAAO,CAAC;IAEzB,yFAAyF;IACzF,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEpC,yCAAyC;IACzC,aAAa,EAAE,kBAAkB,CAAC;CACnC;AAED,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,CAAC;AAC3D,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG,YAAY,GAAG,aAAa,GAAG,kBAAkB,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,iBAAiB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,aAAa,EAAE,CAAC;CAC3B"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * GitHub utilities for Night Watch CLI
3
+ * Fetches PR details using the gh CLI
4
+ */
5
+ export interface PrDetails {
6
+ number: number;
7
+ title: string;
8
+ url: string;
9
+ body: string;
10
+ additions: number;
11
+ deletions: number;
12
+ changedFiles: number;
13
+ }
14
+ /**
15
+ * Fetch PR details for the most recently created PR matching a branch prefix.
16
+ * Returns null if gh is unavailable, not authenticated, or no PR found.
17
+ */
18
+ export declare function fetchPrDetails(branchPrefix: string, cwd: string): PrDetails | null;
19
+ /**
20
+ * Fetch PR details for the most recently updated PR matching any of the given branch patterns.
21
+ * Used by the review command to find the PR that was just reviewed.
22
+ * Returns null on any failure.
23
+ */
24
+ export declare function fetchReviewedPrDetails(branchPatterns: string[], cwd: string): PrDetails | null;
25
+ /**
26
+ * Extract a summary from a PR body.
27
+ * Takes the first meaningful paragraph, strips markdown headers, truncates to maxLength.
28
+ */
29
+ export declare function extractSummary(body: string, maxLength?: number): string;
30
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/utils/github.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAIH,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAyClF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAwC9F;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,GAAE,MAAY,GAAG,MAAM,CAgC5E"}
@@ -0,0 +1,104 @@
1
+ /**
2
+ * GitHub utilities for Night Watch CLI
3
+ * Fetches PR details using the gh CLI
4
+ */
5
+ import { execSync } from "child_process";
6
+ /**
7
+ * Fetch PR details for the most recently created PR matching a branch prefix.
8
+ * Returns null if gh is unavailable, not authenticated, or no PR found.
9
+ */
10
+ export function fetchPrDetails(branchPrefix, cwd) {
11
+ try {
12
+ // Find the most recently created open PR on a matching branch
13
+ const listOutput = execSync(`gh pr list --state open --json number,headRefName --limit 20`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
14
+ const prs = JSON.parse(listOutput);
15
+ // Filter PRs matching the branch prefix
16
+ const matching = prs.filter((pr) => pr.headRefName.startsWith(branchPrefix + "/"));
17
+ if (matching.length === 0) {
18
+ return null;
19
+ }
20
+ // Use the first match (most recent)
21
+ const prNumber = matching[0].number;
22
+ // Fetch full details for this PR
23
+ const viewOutput = execSync(`gh pr view ${prNumber} --json number,title,url,body,additions,deletions,changedFiles`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
24
+ const details = JSON.parse(viewOutput);
25
+ return {
26
+ number: details.number,
27
+ title: details.title ?? "",
28
+ url: details.url ?? "",
29
+ body: details.body ?? "",
30
+ additions: details.additions ?? 0,
31
+ deletions: details.deletions ?? 0,
32
+ changedFiles: details.changedFiles ?? 0,
33
+ };
34
+ }
35
+ catch {
36
+ // gh CLI not available, not authenticated, or command failed
37
+ return null;
38
+ }
39
+ }
40
+ /**
41
+ * Fetch PR details for the most recently updated PR matching any of the given branch patterns.
42
+ * Used by the review command to find the PR that was just reviewed.
43
+ * Returns null on any failure.
44
+ */
45
+ export function fetchReviewedPrDetails(branchPatterns, cwd) {
46
+ try {
47
+ const listOutput = execSync(`gh pr list --state open --json number,headRefName --limit 20`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
48
+ const prs = JSON.parse(listOutput);
49
+ // Filter PRs matching any branch pattern
50
+ const matching = prs.filter((pr) => branchPatterns.some((pattern) => pr.headRefName.startsWith(pattern)));
51
+ if (matching.length === 0) {
52
+ return null;
53
+ }
54
+ // Use the first match
55
+ const prNumber = matching[0].number;
56
+ const viewOutput = execSync(`gh pr view ${prNumber} --json number,title,url,body,additions,deletions,changedFiles`, { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
57
+ const details = JSON.parse(viewOutput);
58
+ return {
59
+ number: details.number,
60
+ title: details.title ?? "",
61
+ url: details.url ?? "",
62
+ body: details.body ?? "",
63
+ additions: details.additions ?? 0,
64
+ deletions: details.deletions ?? 0,
65
+ changedFiles: details.changedFiles ?? 0,
66
+ };
67
+ }
68
+ catch {
69
+ return null;
70
+ }
71
+ }
72
+ /**
73
+ * Extract a summary from a PR body.
74
+ * Takes the first meaningful paragraph, strips markdown headers, truncates to maxLength.
75
+ */
76
+ export function extractSummary(body, maxLength = 500) {
77
+ if (!body || body.trim().length === 0) {
78
+ return "";
79
+ }
80
+ // Split into lines and filter out markdown headers and empty lines
81
+ const lines = body.split("\n");
82
+ const contentLines = [];
83
+ for (const line of lines) {
84
+ const trimmed = line.trim();
85
+ // Skip empty lines, markdown headers, and horizontal rules
86
+ if (trimmed === "" || trimmed.startsWith("#") || trimmed === "---" || trimmed === "***") {
87
+ // If we already have content, a blank line means end of first paragraph
88
+ if (contentLines.length > 0 && trimmed === "") {
89
+ break;
90
+ }
91
+ continue;
92
+ }
93
+ contentLines.push(trimmed);
94
+ }
95
+ const summary = contentLines.join("\n");
96
+ if (summary.length <= maxLength) {
97
+ return summary;
98
+ }
99
+ // Truncate at word boundary
100
+ const truncated = summary.slice(0, maxLength);
101
+ const lastSpace = truncated.lastIndexOf(" ");
102
+ return (lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated) + "...";
103
+ }
104
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/utils/github.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Notification utilities for Night Watch CLI
3
+ * Sends webhook notifications to Slack, Discord, and Telegram
4
+ */
5
+ import { INightWatchConfig, WebhookConfig, NotificationEvent } from "../types.js";
6
+ export interface NotificationContext {
7
+ event: NotificationEvent;
8
+ projectName: string;
9
+ prdName?: string;
10
+ branchName?: string;
11
+ prNumber?: number;
12
+ exitCode: number;
13
+ duration?: number;
14
+ provider: string;
15
+ prUrl?: string;
16
+ prTitle?: string;
17
+ prBody?: string;
18
+ filesChanged?: number;
19
+ additions?: number;
20
+ deletions?: number;
21
+ }
22
+ /**
23
+ * Get the emoji for a notification event
24
+ */
25
+ export declare function getEventEmoji(event: NotificationEvent): string;
26
+ /**
27
+ * Get a human-readable title for a notification event
28
+ */
29
+ export declare function getEventTitle(event: NotificationEvent): string;
30
+ /**
31
+ * Get the Discord embed color for a notification event
32
+ */
33
+ export declare function getEventColor(event: NotificationEvent): number;
34
+ /**
35
+ * Build a description string from notification context
36
+ */
37
+ export declare function buildDescription(ctx: NotificationContext): string;
38
+ /**
39
+ * Format a notification payload for Slack incoming webhooks
40
+ */
41
+ export declare function formatSlackPayload(ctx: NotificationContext): object;
42
+ /**
43
+ * Format a notification payload for Discord webhooks
44
+ */
45
+ export declare function formatDiscordPayload(ctx: NotificationContext): object;
46
+ /**
47
+ * Build a structured Telegram message when PR details are available.
48
+ * Falls back to the basic format when they are not.
49
+ */
50
+ export declare function formatTelegramPayload(ctx: NotificationContext): {
51
+ text: string;
52
+ parse_mode: string;
53
+ };
54
+ /**
55
+ * Send a notification to a single webhook endpoint
56
+ * Silently catches errors — never throws
57
+ */
58
+ export declare function sendWebhook(webhook: WebhookConfig, ctx: NotificationContext): Promise<void>;
59
+ /**
60
+ * Send notifications to all configured webhooks
61
+ */
62
+ export declare function sendNotifications(config: INightWatchConfig, ctx: NotificationContext): Promise<void>;
63
+ //# sourceMappingURL=notify.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.d.ts","sourceRoot":"","sources":["../../src/utils/notify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAIlF,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,iBAAiB,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IAEjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAW9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAW9D;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,MAAM,CAW9D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAkBjE;AASD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CA8BnE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,mBAAmB,GAAG,MAAM,CAerE;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,mBAAmB,GAAG;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;CACpB,CAqDA;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAAC,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC,CAsCjG;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,iBAAiB,EACzB,GAAG,EAAE,mBAAmB,GACvB,OAAO,CAAC,IAAI,CAAC,CAWf"}
@@ -0,0 +1,237 @@
1
+ /**
2
+ * Notification utilities for Night Watch CLI
3
+ * Sends webhook notifications to Slack, Discord, and Telegram
4
+ */
5
+ import { warn, info } from "./ui.js";
6
+ import { extractSummary } from "./github.js";
7
+ /**
8
+ * Get the emoji for a notification event
9
+ */
10
+ export function getEventEmoji(event) {
11
+ switch (event) {
12
+ case "run_succeeded":
13
+ return "\u2705";
14
+ case "run_failed":
15
+ return "\u274C";
16
+ case "run_timeout":
17
+ return "\u23F0";
18
+ case "review_completed":
19
+ return "\uD83D\uDD0D";
20
+ }
21
+ }
22
+ /**
23
+ * Get a human-readable title for a notification event
24
+ */
25
+ export function getEventTitle(event) {
26
+ switch (event) {
27
+ case "run_succeeded":
28
+ return "PRD Execution Succeeded";
29
+ case "run_failed":
30
+ return "PRD Execution Failed";
31
+ case "run_timeout":
32
+ return "PRD Execution Timed Out";
33
+ case "review_completed":
34
+ return "PR Review Completed";
35
+ }
36
+ }
37
+ /**
38
+ * Get the Discord embed color for a notification event
39
+ */
40
+ export function getEventColor(event) {
41
+ switch (event) {
42
+ case "run_succeeded":
43
+ return 0x00ff00;
44
+ case "run_failed":
45
+ return 0xff0000;
46
+ case "run_timeout":
47
+ return 0xff0000;
48
+ case "review_completed":
49
+ return 0x0099ff;
50
+ }
51
+ }
52
+ /**
53
+ * Build a description string from notification context
54
+ */
55
+ export function buildDescription(ctx) {
56
+ const lines = [];
57
+ lines.push(`Project: ${ctx.projectName}`);
58
+ lines.push(`Provider: ${ctx.provider}`);
59
+ lines.push(`Exit code: ${ctx.exitCode}`);
60
+ if (ctx.prdName) {
61
+ lines.push(`PRD: ${ctx.prdName}`);
62
+ }
63
+ if (ctx.branchName) {
64
+ lines.push(`Branch: ${ctx.branchName}`);
65
+ }
66
+ if (ctx.prNumber !== undefined) {
67
+ lines.push(`PR: #${ctx.prNumber}`);
68
+ }
69
+ if (ctx.duration !== undefined) {
70
+ lines.push(`Duration: ${ctx.duration}s`);
71
+ }
72
+ return lines.join("\n");
73
+ }
74
+ /**
75
+ * Escape special characters for Telegram MarkdownV2 format
76
+ */
77
+ function escapeMarkdownV2(text) {
78
+ return text.replace(/[_*[\]()~`>#+=|{}.!-]/g, "\\$&");
79
+ }
80
+ /**
81
+ * Format a notification payload for Slack incoming webhooks
82
+ */
83
+ export function formatSlackPayload(ctx) {
84
+ const emoji = getEventEmoji(ctx.event);
85
+ const title = getEventTitle(ctx.event);
86
+ const description = buildDescription(ctx);
87
+ let color;
88
+ if (ctx.event === "run_succeeded") {
89
+ color = "#00ff00";
90
+ }
91
+ else if (ctx.event === "review_completed") {
92
+ color = "#0099ff";
93
+ }
94
+ else {
95
+ color = "#ff0000";
96
+ }
97
+ return {
98
+ attachments: [
99
+ {
100
+ color,
101
+ blocks: [
102
+ {
103
+ type: "section",
104
+ text: {
105
+ type: "mrkdwn",
106
+ text: `*${emoji} ${title}*\n${description}`,
107
+ },
108
+ },
109
+ ],
110
+ },
111
+ ],
112
+ };
113
+ }
114
+ /**
115
+ * Format a notification payload for Discord webhooks
116
+ */
117
+ export function formatDiscordPayload(ctx) {
118
+ const emoji = getEventEmoji(ctx.event);
119
+ const title = getEventTitle(ctx.event);
120
+ const description = buildDescription(ctx);
121
+ return {
122
+ embeds: [
123
+ {
124
+ title: `${emoji} ${title}`,
125
+ description,
126
+ color: getEventColor(ctx.event),
127
+ timestamp: new Date().toISOString(),
128
+ },
129
+ ],
130
+ };
131
+ }
132
+ /**
133
+ * Build a structured Telegram message when PR details are available.
134
+ * Falls back to the basic format when they are not.
135
+ */
136
+ export function formatTelegramPayload(ctx) {
137
+ const emoji = getEventEmoji(ctx.event);
138
+ const title = getEventTitle(ctx.event);
139
+ // If PR details are present, use the rich structured template
140
+ if (ctx.prUrl && ctx.prTitle) {
141
+ const lines = [];
142
+ lines.push(`*${escapeMarkdownV2(emoji + " " + title)}*`);
143
+ lines.push("");
144
+ lines.push(`${escapeMarkdownV2("📋")} *${escapeMarkdownV2("PR #" + (ctx.prNumber ?? "") + ": " + ctx.prTitle)}*`);
145
+ lines.push(`${escapeMarkdownV2("🔗")} ${escapeMarkdownV2(ctx.prUrl)}`);
146
+ // Summary from PR body
147
+ if (ctx.prBody && ctx.prBody.trim().length > 0) {
148
+ const summary = extractSummary(ctx.prBody);
149
+ if (summary) {
150
+ lines.push("");
151
+ lines.push(`${escapeMarkdownV2("📝 Summary")}`);
152
+ lines.push(escapeMarkdownV2(summary));
153
+ }
154
+ }
155
+ // Stats
156
+ if (ctx.filesChanged !== undefined || ctx.additions !== undefined) {
157
+ lines.push("");
158
+ lines.push(`${escapeMarkdownV2("📊 Stats")}`);
159
+ const stats = [];
160
+ if (ctx.filesChanged !== undefined) {
161
+ stats.push(`Files changed: ${ctx.filesChanged}`);
162
+ }
163
+ if (ctx.additions !== undefined && ctx.deletions !== undefined) {
164
+ stats.push(`+${ctx.additions} / -${ctx.deletions}`);
165
+ }
166
+ lines.push(escapeMarkdownV2(stats.join(" | ")));
167
+ }
168
+ // Footer
169
+ lines.push("");
170
+ lines.push(escapeMarkdownV2(`⚙️ Project: ${ctx.projectName} | Provider: ${ctx.provider}`));
171
+ return {
172
+ text: lines.join("\n"),
173
+ parse_mode: "MarkdownV2",
174
+ };
175
+ }
176
+ // Fallback: basic format (no PR details)
177
+ const description = buildDescription(ctx);
178
+ return {
179
+ text: `*${escapeMarkdownV2(emoji + " " + title)}*\n\n${escapeMarkdownV2(description)}`,
180
+ parse_mode: "MarkdownV2",
181
+ };
182
+ }
183
+ /**
184
+ * Send a notification to a single webhook endpoint
185
+ * Silently catches errors — never throws
186
+ */
187
+ export async function sendWebhook(webhook, ctx) {
188
+ // Skip if this event is not in the webhook's configured events
189
+ if (!webhook.events.includes(ctx.event)) {
190
+ return;
191
+ }
192
+ try {
193
+ let url;
194
+ let body;
195
+ switch (webhook.type) {
196
+ case "slack": {
197
+ url = webhook.url;
198
+ body = JSON.stringify(formatSlackPayload(ctx));
199
+ break;
200
+ }
201
+ case "discord": {
202
+ url = webhook.url;
203
+ body = JSON.stringify(formatDiscordPayload(ctx));
204
+ break;
205
+ }
206
+ case "telegram": {
207
+ url = `https://api.telegram.org/bot${webhook.botToken}/sendMessage`;
208
+ const telegramPayload = formatTelegramPayload(ctx);
209
+ body = JSON.stringify({ chat_id: webhook.chatId, ...telegramPayload });
210
+ break;
211
+ }
212
+ }
213
+ await fetch(url, {
214
+ method: "POST",
215
+ headers: { "Content-Type": "application/json" },
216
+ body,
217
+ });
218
+ }
219
+ catch (err) {
220
+ const message = err instanceof Error ? err.message : String(err);
221
+ warn(`Notification failed (${webhook.type}): ${message}`);
222
+ }
223
+ }
224
+ /**
225
+ * Send notifications to all configured webhooks
226
+ */
227
+ export async function sendNotifications(config, ctx) {
228
+ if (!config.notifications || config.notifications.webhooks.length === 0) {
229
+ return;
230
+ }
231
+ const webhooks = config.notifications.webhooks;
232
+ const results = await Promise.allSettled(webhooks.map((wh) => sendWebhook(wh, ctx)));
233
+ const sent = results.filter((r) => r.status === "fulfilled").length;
234
+ const total = webhooks.length;
235
+ info(`Sent ${sent}/${total} notifications`);
236
+ }
237
+ //# sourceMappingURL=notify.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notify.js","sourceRoot":"","sources":["../../src/utils/notify.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAoB7C;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB;IACpD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,QAAQ,CAAC;QAClB,KAAK,aAAa;YAChB,OAAO,QAAQ,CAAC;QAClB,KAAK,kBAAkB;YACrB,OAAO,cAAc,CAAC;IAC1B,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB;IACpD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,eAAe;YAClB,OAAO,yBAAyB,CAAC;QACnC,KAAK,YAAY;YACf,OAAO,sBAAsB,CAAC;QAChC,KAAK,aAAa;YAChB,OAAO,yBAAyB,CAAC;QACnC,KAAK,kBAAkB;YACrB,OAAO,qBAAqB,CAAC;IACjC,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,aAAa,CAAC,KAAwB;IACpD,QAAQ,KAAK,EAAE,CAAC;QACd,KAAK,eAAe;YAClB,OAAO,QAAQ,CAAC;QAClB,KAAK,YAAY;YACf,OAAO,QAAQ,CAAC;QAClB,KAAK,aAAa;YAChB,OAAO,QAAQ,CAAC;QAClB,KAAK,kBAAkB;YACrB,OAAO,QAAQ,CAAC;IACpB,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,GAAwB;IACvD,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,YAAY,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC1C,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACxC,KAAK,CAAC,IAAI,CAAC,cAAc,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACzC,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAChB,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IACpC,CAAC;IACD,IAAI,GAAG,CAAC,UAAU,EAAE,CAAC;QACnB,KAAK,CAAC,IAAI,CAAC,WAAW,GAAG,CAAC,UAAU,EAAE,CAAC,CAAC;IAC1C,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,QAAQ,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;IACrC,CAAC;IACD,IAAI,GAAG,CAAC,QAAQ,KAAK,SAAS,EAAE,CAAC;QAC/B,KAAK,CAAC,IAAI,CAAC,aAAa,GAAG,CAAC,QAAQ,GAAG,CAAC,CAAC;IAC3C,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,SAAS,gBAAgB,CAAC,IAAY;IACpC,OAAO,IAAI,CAAC,OAAO,CAAC,wBAAwB,EAAE,MAAM,CAAC,CAAC;AACxD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAAC,GAAwB;IACzD,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAE1C,IAAI,KAAa,CAAC;IAClB,IAAI,GAAG,CAAC,KAAK,KAAK,eAAe,EAAE,CAAC;QAClC,KAAK,GAAG,SAAS,CAAC;IACpB,CAAC;SAAM,IAAI,GAAG,CAAC,KAAK,KAAK,kBAAkB,EAAE,CAAC;QAC5C,KAAK,GAAG,SAAS,CAAC;IACpB,CAAC;SAAM,CAAC;QACN,KAAK,GAAG,SAAS,CAAC;IACpB,CAAC;IAED,OAAO;QACL,WAAW,EAAE;YACX;gBACE,KAAK;gBACL,MAAM,EAAE;oBACN;wBACE,IAAI,EAAE,SAAS;wBACf,IAAI,EAAE;4BACJ,IAAI,EAAE,QAAQ;4BACd,IAAI,EAAE,IAAI,KAAK,IAAI,KAAK,MAAM,WAAW,EAAE;yBAC5C;qBACF;iBACF;aACF;SACF;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAwB;IAC3D,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAE1C,OAAO;QACL,MAAM,EAAE;YACN;gBACE,KAAK,EAAE,GAAG,KAAK,IAAI,KAAK,EAAE;gBAC1B,WAAW;gBACX,KAAK,EAAE,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC;gBAC/B,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;aACpC;SACF;KACF,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,GAAwB;IAI5D,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IACvC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAEvC,8DAA8D;IAC9D,IAAI,GAAG,CAAC,KAAK,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC7B,MAAM,KAAK,GAAa,EAAE,CAAC;QAE3B,KAAK,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC;QACzD,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,KAAK,gBAAgB,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,GAAG,IAAI,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAClH,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,IAAI,CAAC,IAAI,gBAAgB,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAEvE,uBAAuB;QACvB,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YAC3C,IAAI,OAAO,EAAE,CAAC;gBACZ,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACf,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;gBAChD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,OAAO,CAAC,CAAC,CAAC;YACxC,CAAC;QACH,CAAC;QAED,QAAQ;QACR,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;YAClE,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACf,KAAK,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;YAC9C,MAAM,KAAK,GAAa,EAAE,CAAC;YAC3B,IAAI,GAAG,CAAC,YAAY,KAAK,SAAS,EAAE,CAAC;gBACnC,KAAK,CAAC,IAAI,CAAC,kBAAkB,GAAG,CAAC,YAAY,EAAE,CAAC,CAAC;YACnD,CAAC;YACD,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,IAAI,GAAG,CAAC,SAAS,KAAK,SAAS,EAAE,CAAC;gBAC/D,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,SAAS,OAAO,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC;YACtD,CAAC;YACD,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,SAAS;QACT,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACf,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,eAAe,GAAG,CAAC,WAAW,gBAAgB,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;QAE3F,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;YACtB,UAAU,EAAE,YAAY;SACzB,CAAC;IACJ,CAAC;IAED,yCAAyC;IACzC,MAAM,WAAW,GAAG,gBAAgB,CAAC,GAAG,CAAC,CAAC;IAC1C,OAAO;QACL,IAAI,EAAE,IAAI,gBAAgB,CAAC,KAAK,GAAG,GAAG,GAAG,KAAK,CAAC,QAAQ,gBAAgB,CAAC,WAAW,CAAC,EAAE;QACtF,UAAU,EAAE,YAAY;KACzB,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAsB,EAAE,GAAwB;IAChF,+DAA+D;IAC/D,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO;IACT,CAAC;IAED,IAAI,CAAC;QACH,IAAI,GAAW,CAAC;QAChB,IAAI,IAAY,CAAC;QAEjB,QAAQ,OAAO,CAAC,IAAI,EAAE,CAAC;YACrB,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,GAAG,GAAG,OAAO,CAAC,GAAI,CAAC;gBACnB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC/C,MAAM;YACR,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,GAAG,GAAG,OAAO,CAAC,GAAI,CAAC;gBACnB,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,oBAAoB,CAAC,GAAG,CAAC,CAAC,CAAC;gBACjD,MAAM;YACR,CAAC;YACD,KAAK,UAAU,CAAC,CAAC,CAAC;gBAChB,GAAG,GAAG,+BAA+B,OAAO,CAAC,QAAQ,cAAc,CAAC;gBACpE,MAAM,eAAe,GAAG,qBAAqB,CAAC,GAAG,CAAC,CAAC;gBACnD,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,GAAG,eAAe,EAAE,CAAC,CAAC;gBACvE,MAAM;YACR,CAAC;QACH,CAAC;QAED,MAAM,KAAK,CAAC,GAAG,EAAE;YACf,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,MAAM,OAAO,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjE,IAAI,CAAC,wBAAwB,OAAO,CAAC,IAAI,MAAM,OAAO,EAAE,CAAC,CAAC;IAC5D,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,MAAyB,EACzB,GAAwB;IAExB,IAAI,CAAC,MAAM,CAAC,aAAa,IAAI,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxE,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC;IAC/C,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,WAAW,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC;IAErF,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;IACpE,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC;IAC9B,IAAI,CAAC,QAAQ,IAAI,IAAI,KAAK,gBAAgB,CAAC,CAAC;AAC9C,CAAC"}