portable-agent-layer 0.30.1 → 0.32.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/skills/consulting-report/SKILL.md +74 -74
- package/assets/skills/consulting-report/demo/app/globals.css +459 -0
- package/assets/skills/consulting-report/demo/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/demo/app/page.tsx +302 -0
- package/assets/skills/consulting-report/demo/bun.lock +240 -0
- package/assets/skills/consulting-report/demo/components/callout.tsx +13 -0
- package/assets/skills/consulting-report/demo/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/demo/components/cover-page.tsx +34 -0
- package/assets/skills/consulting-report/demo/components/exhibit.tsx +21 -0
- package/assets/skills/consulting-report/demo/components/finding-card.tsx +28 -0
- package/assets/skills/consulting-report/demo/components/quote-block.tsx +17 -0
- package/assets/skills/consulting-report/demo/components/recommendation-card.tsx +52 -0
- package/assets/skills/consulting-report/demo/components/section.tsx +17 -0
- package/assets/skills/consulting-report/demo/components/severity-badge.tsx +19 -0
- package/assets/skills/consulting-report/demo/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/demo/components/table-of-contents.tsx +27 -0
- package/assets/skills/consulting-report/demo/components/timeline.tsx +20 -0
- package/assets/skills/consulting-report/demo/lib/report-data.ts +247 -0
- package/assets/skills/consulting-report/demo/lib/utils.ts +6 -0
- package/assets/skills/consulting-report/demo/next.config.js +9 -0
- package/assets/skills/consulting-report/demo/package.json +27 -0
- package/assets/skills/consulting-report/demo/postcss.config.mjs +5 -0
- package/assets/skills/consulting-report/demo/tsconfig.json +41 -0
- package/assets/skills/consulting-report/template/app/globals.css +459 -0
- package/assets/skills/consulting-report/template/app/layout.tsx +32 -0
- package/assets/skills/consulting-report/template/app/page.tsx +282 -0
- package/assets/skills/consulting-report/template/bun.lock +240 -0
- package/assets/skills/consulting-report/template/components/callout.tsx +13 -0
- package/assets/skills/consulting-report/template/components/comparison-table.tsx +40 -0
- package/assets/skills/consulting-report/template/components/cover-page.tsx +34 -0
- package/assets/skills/consulting-report/template/components/exhibit.tsx +21 -0
- package/assets/skills/consulting-report/template/components/finding-card.tsx +28 -0
- package/assets/skills/consulting-report/template/components/quote-block.tsx +17 -0
- package/assets/skills/consulting-report/template/components/recommendation-card.tsx +52 -0
- package/assets/skills/consulting-report/template/components/section.tsx +17 -0
- package/assets/skills/consulting-report/template/components/severity-badge.tsx +19 -0
- package/assets/skills/consulting-report/template/components/stat-grid.tsx +26 -0
- package/assets/skills/consulting-report/template/components/table-of-contents.tsx +27 -0
- package/assets/skills/consulting-report/template/components/timeline.tsx +20 -0
- package/assets/skills/consulting-report/template/lib/report-data.ts +176 -0
- package/assets/skills/consulting-report/template/lib/utils.ts +6 -0
- package/assets/skills/consulting-report/template/next.config.js +9 -0
- package/assets/skills/consulting-report/template/package.json +27 -0
- package/assets/skills/consulting-report/template/postcss.config.mjs +5 -0
- package/assets/skills/consulting-report/template/tsconfig.json +27 -0
- package/assets/skills/consulting-report/tools/dev.ts +47 -0
- package/assets/skills/consulting-report/tools/generate-pdf.ts +140 -408
- package/assets/skills/consulting-report/tools/scaffold.ts +83 -48
- package/assets/skills/presentation/SKILL.md +1 -1
- package/package.json +9 -9
- package/assets/skills/consulting-report/demo/content/current-state.md +0 -33
- package/assets/skills/consulting-report/demo/content/executive-summary.md +0 -19
- package/assets/skills/consulting-report/demo/content/report-data.ts +0 -101
- package/assets/skills/consulting-report/demo/diagrams/.gitkeep +0 -0
- package/assets/skills/consulting-report/template/README.md +0 -28
- package/assets/skills/consulting-report/template/content/executive-summary.md +0 -19
- package/assets/skills/consulting-report/template/content/report-data.ts +0 -59
- package/assets/skills/consulting-report/template/diagrams/.gitkeep +0 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
interface ExhibitProps {
|
|
2
|
+
number: number | string;
|
|
3
|
+
title: string;
|
|
4
|
+
source?: string;
|
|
5
|
+
children: React.ReactNode;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function Exhibit({ number, title, source, children }: ExhibitProps) {
|
|
9
|
+
return (
|
|
10
|
+
<div className="exhibit">
|
|
11
|
+
<div className="exhibit-header">
|
|
12
|
+
<div>
|
|
13
|
+
<span className="exhibit-number">Exhibit {number}</span>
|
|
14
|
+
<span className="exhibit-title ml-3">{title}</span>
|
|
15
|
+
</div>
|
|
16
|
+
{source && <span className="text-xs text-muted italic">Source: {source}</span>}
|
|
17
|
+
</div>
|
|
18
|
+
<div className="exhibit-content">{children}</div>
|
|
19
|
+
</div>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import type { Finding } from "@/lib/report-data";
|
|
2
|
+
import { SeverityBadge } from "./severity-badge";
|
|
3
|
+
|
|
4
|
+
interface FindingCardProps {
|
|
5
|
+
finding: Finding;
|
|
6
|
+
index: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function FindingCard({ finding, index }: FindingCardProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="finding-card">
|
|
12
|
+
<div className="finding-header">
|
|
13
|
+
<div className="flex items-center gap-4">
|
|
14
|
+
<span className="text-primary font-bold font-sans text-2xl min-w-8">
|
|
15
|
+
{index + 1}.
|
|
16
|
+
</span>
|
|
17
|
+
<span className="finding-title">{finding.title}</span>
|
|
18
|
+
</div>
|
|
19
|
+
<SeverityBadge severity={finding.severity} />
|
|
20
|
+
</div>
|
|
21
|
+
<p className="text-foreground mb-2 ml-12">{finding.description}</p>
|
|
22
|
+
<p className="finding-evidence ml-12">
|
|
23
|
+
<span className="font-medium text-foreground">Evidence:</span> {finding.evidence}
|
|
24
|
+
</p>
|
|
25
|
+
<p className="text-xs text-muted mt-2 italic ml-12">Source: {finding.source}</p>
|
|
26
|
+
</div>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
interface QuoteBlockProps {
|
|
2
|
+
quote: string;
|
|
3
|
+
attribution: string;
|
|
4
|
+
role?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function QuoteBlock({ quote, attribution, role }: QuoteBlockProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="quote-block">
|
|
10
|
+
<p className="quote-text">{quote}</p>
|
|
11
|
+
<p className="quote-attribution">
|
|
12
|
+
— {attribution}
|
|
13
|
+
{role && <span className="text-muted">, {role}</span>}
|
|
14
|
+
</p>
|
|
15
|
+
</div>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ArrowRight, Clock, Zap } from "lucide-react";
|
|
2
|
+
import type { Recommendation } from "@/lib/report-data";
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
|
|
5
|
+
interface RecommendationCardProps {
|
|
6
|
+
recommendation: Recommendation;
|
|
7
|
+
index: number;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const priorityConfig = {
|
|
11
|
+
immediate: {
|
|
12
|
+
icon: Zap,
|
|
13
|
+
label: "Immediate",
|
|
14
|
+
className: "bg-destructive/10 text-destructive border-destructive/20",
|
|
15
|
+
},
|
|
16
|
+
"short-term": {
|
|
17
|
+
icon: Clock,
|
|
18
|
+
label: "Short-term",
|
|
19
|
+
className: "bg-warning/10 text-warning border-warning/20",
|
|
20
|
+
},
|
|
21
|
+
"long-term": {
|
|
22
|
+
icon: ArrowRight,
|
|
23
|
+
label: "Long-term",
|
|
24
|
+
className: "bg-primary/10 text-primary border-primary/20",
|
|
25
|
+
},
|
|
26
|
+
} as const;
|
|
27
|
+
|
|
28
|
+
export function RecommendationCard({ recommendation, index }: RecommendationCardProps) {
|
|
29
|
+
const config = priorityConfig[recommendation.priority];
|
|
30
|
+
const Icon = config.icon;
|
|
31
|
+
|
|
32
|
+
return (
|
|
33
|
+
<div className="finding-card">
|
|
34
|
+
<div className="finding-header">
|
|
35
|
+
<div className="flex items-start gap-3">
|
|
36
|
+
<span className="text-primary font-bold font-sans text-lg">{index + 1}</span>
|
|
37
|
+
<span className="finding-title">{recommendation.title}</span>
|
|
38
|
+
</div>
|
|
39
|
+
<span
|
|
40
|
+
className={cn(
|
|
41
|
+
"inline-flex items-center gap-1.5 px-3 py-1 rounded-full text-xs font-semibold border",
|
|
42
|
+
config.className
|
|
43
|
+
)}
|
|
44
|
+
>
|
|
45
|
+
<Icon className="h-3 w-3" />
|
|
46
|
+
{config.label}
|
|
47
|
+
</span>
|
|
48
|
+
</div>
|
|
49
|
+
<p className="text-foreground ml-8">{recommendation.description}</p>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
|
|
3
|
+
interface SectionProps {
|
|
4
|
+
id?: string;
|
|
5
|
+
title: string;
|
|
6
|
+
children: React.ReactNode;
|
|
7
|
+
className?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function Section({ id, title, children, className }: SectionProps) {
|
|
11
|
+
return (
|
|
12
|
+
<section id={id} className={cn("report-section", className)}>
|
|
13
|
+
<h2>{title}</h2>
|
|
14
|
+
{children}
|
|
15
|
+
</section>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { cn } from "@/lib/utils";
|
|
2
|
+
|
|
3
|
+
type Severity = "critical" | "high" | "medium" | "low";
|
|
4
|
+
|
|
5
|
+
interface SeverityBadgeProps {
|
|
6
|
+
severity: Severity;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const severityConfig: Record<Severity, { label: string; className: string }> = {
|
|
10
|
+
critical: { label: "Critical", className: "severity-critical" },
|
|
11
|
+
high: { label: "High", className: "severity-high" },
|
|
12
|
+
medium: { label: "Medium", className: "severity-medium" },
|
|
13
|
+
low: { label: "Low", className: "severity-low" },
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export function SeverityBadge({ severity }: SeverityBadgeProps) {
|
|
17
|
+
const config = severityConfig[severity];
|
|
18
|
+
return <span className={cn("severity-badge", config.className)}>{config.label}</span>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
interface Stat {
|
|
2
|
+
value: string;
|
|
3
|
+
label: string;
|
|
4
|
+
caption?: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface StatGridProps {
|
|
8
|
+
stats: Stat[];
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function StatGrid({ stats }: StatGridProps) {
|
|
12
|
+
return (
|
|
13
|
+
<div
|
|
14
|
+
className="stat-grid"
|
|
15
|
+
style={{ gridTemplateColumns: `repeat(${stats.length}, minmax(0, 1fr))` }}
|
|
16
|
+
>
|
|
17
|
+
{stats.map((s) => (
|
|
18
|
+
<div key={s.label} className="stat">
|
|
19
|
+
<div className="stat-value">{s.value}</div>
|
|
20
|
+
<div className="stat-label">{s.label}</div>
|
|
21
|
+
{s.caption && <div className="stat-caption">{s.caption}</div>}
|
|
22
|
+
</div>
|
|
23
|
+
))}
|
|
24
|
+
</div>
|
|
25
|
+
);
|
|
26
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
interface TocItem {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
interface TableOfContentsProps {
|
|
7
|
+
items: TocItem[];
|
|
8
|
+
title?: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function TableOfContents({ items, title = "Contents" }: TableOfContentsProps) {
|
|
12
|
+
return (
|
|
13
|
+
<nav className="toc">
|
|
14
|
+
<h2>{title}</h2>
|
|
15
|
+
<ol>
|
|
16
|
+
{items.map((item, i) => (
|
|
17
|
+
<li key={item.id}>
|
|
18
|
+
<a href={`#${item.id}`}>
|
|
19
|
+
<span className="toc-number">{(i + 1).toString().padStart(2, "0")}</span>
|
|
20
|
+
<span className="toc-title">{item.title}</span>
|
|
21
|
+
</a>
|
|
22
|
+
</li>
|
|
23
|
+
))}
|
|
24
|
+
</ol>
|
|
25
|
+
</nav>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { TimelinePhase } from "@/lib/report-data";
|
|
2
|
+
|
|
3
|
+
interface TimelineProps {
|
|
4
|
+
phases: TimelinePhase[];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function Timeline({ phases }: TimelineProps) {
|
|
8
|
+
return (
|
|
9
|
+
<div className="timeline">
|
|
10
|
+
{phases.map((phase) => (
|
|
11
|
+
<div key={`${phase.phase}-${phase.title}`} className="timeline-item">
|
|
12
|
+
<div className="timeline-phase">{phase.phase}</div>
|
|
13
|
+
<div className="timeline-title">{phase.title}</div>
|
|
14
|
+
<div className="timeline-description">{phase.description}</div>
|
|
15
|
+
<div className="text-xs text-primary font-medium mt-1">{phase.duration}</div>
|
|
16
|
+
</div>
|
|
17
|
+
))}
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
export interface Finding {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
evidence: string;
|
|
6
|
+
source: string;
|
|
7
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface Recommendation {
|
|
11
|
+
id: string;
|
|
12
|
+
title: string;
|
|
13
|
+
description: string;
|
|
14
|
+
priority: "immediate" | "short-term" | "long-term";
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface TimelinePhase {
|
|
18
|
+
phase: string;
|
|
19
|
+
title: string;
|
|
20
|
+
description: string;
|
|
21
|
+
duration: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface ReportData {
|
|
25
|
+
clientName: string;
|
|
26
|
+
reportTitle: string;
|
|
27
|
+
reportDate: string;
|
|
28
|
+
classification: string;
|
|
29
|
+
consultancyName: string;
|
|
30
|
+
preTitle?: string;
|
|
31
|
+
|
|
32
|
+
executiveSummary: {
|
|
33
|
+
context: string;
|
|
34
|
+
methodology: { interviewCount: number; roles: string[] };
|
|
35
|
+
keyFindings: string[];
|
|
36
|
+
primaryRecommendation: string;
|
|
37
|
+
expectedOutcomes: string[];
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
situationAssessment: {
|
|
41
|
+
currentState: string;
|
|
42
|
+
clientAsk: string;
|
|
43
|
+
whyNow: string;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
findings: Finding[];
|
|
47
|
+
|
|
48
|
+
riskAnalysis: {
|
|
49
|
+
existentialRisks: string[];
|
|
50
|
+
competitiveThreats: string[];
|
|
51
|
+
timelinePressures: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
strategicOpportunity: {
|
|
55
|
+
goodNews: string;
|
|
56
|
+
requirements: string[];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
recommendations: Recommendation[];
|
|
60
|
+
|
|
61
|
+
targetState: {
|
|
62
|
+
description: string;
|
|
63
|
+
keyCapabilities: string[];
|
|
64
|
+
successMetrics: string[];
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
roadmap: TimelinePhase[];
|
|
68
|
+
|
|
69
|
+
callToAction: {
|
|
70
|
+
immediateSteps: string[];
|
|
71
|
+
decisionPoints: string[];
|
|
72
|
+
commitmentRequired: string;
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export const reportData: ReportData = {
|
|
77
|
+
clientName: "[CLIENT NAME]",
|
|
78
|
+
reportTitle: "Strategic Assessment & Transformation Roadmap",
|
|
79
|
+
reportDate: new Date().toLocaleDateString("en-US", {
|
|
80
|
+
year: "numeric",
|
|
81
|
+
month: "long",
|
|
82
|
+
day: "numeric",
|
|
83
|
+
}),
|
|
84
|
+
classification: "CONFIDENTIAL",
|
|
85
|
+
consultancyName: "[YOUR CONSULTANCY]",
|
|
86
|
+
preTitle: "Strategic Assessment",
|
|
87
|
+
|
|
88
|
+
executiveSummary: {
|
|
89
|
+
context:
|
|
90
|
+
"This report presents findings from a comprehensive assessment of [CLIENT], conducted to evaluate strategic readiness and identify transformation opportunities.",
|
|
91
|
+
methodology: {
|
|
92
|
+
interviewCount: 0,
|
|
93
|
+
roles: ["Executive Leadership", "Department Heads", "Team Leads"],
|
|
94
|
+
},
|
|
95
|
+
keyFindings: [
|
|
96
|
+
"Finding 1 — replace with actual finding",
|
|
97
|
+
"Finding 2 — replace with actual finding",
|
|
98
|
+
"Finding 3 — replace with actual finding",
|
|
99
|
+
],
|
|
100
|
+
primaryRecommendation: "Based on this analysis, [PRIMARY RECOMMENDATION HERE]",
|
|
101
|
+
expectedOutcomes: ["Expected outcome 1", "Expected outcome 2", "Expected outcome 3"],
|
|
102
|
+
},
|
|
103
|
+
|
|
104
|
+
situationAssessment: {
|
|
105
|
+
currentState: "Replace with current state analysis.",
|
|
106
|
+
clientAsk: "Replace with what the client originally asked for.",
|
|
107
|
+
whyNow: "Replace with why this matters now — the underlying drivers.",
|
|
108
|
+
},
|
|
109
|
+
|
|
110
|
+
findings: [
|
|
111
|
+
{
|
|
112
|
+
id: "F1",
|
|
113
|
+
title: "Finding Title",
|
|
114
|
+
description: "Description of the finding.",
|
|
115
|
+
evidence: "Evidence supporting this finding.",
|
|
116
|
+
source: "Interview / data / observation",
|
|
117
|
+
severity: "critical",
|
|
118
|
+
},
|
|
119
|
+
],
|
|
120
|
+
|
|
121
|
+
riskAnalysis: {
|
|
122
|
+
existentialRisks: ["Replace with existential risks"],
|
|
123
|
+
competitiveThreats: ["Replace with competitive threats"],
|
|
124
|
+
timelinePressures: "Replace with timeline pressures and urgency factors.",
|
|
125
|
+
},
|
|
126
|
+
|
|
127
|
+
strategicOpportunity: {
|
|
128
|
+
goodNews: "Replace with the strategic pivot — the path forward.",
|
|
129
|
+
requirements: ["Requirement 1", "Requirement 2", "Requirement 3"],
|
|
130
|
+
},
|
|
131
|
+
|
|
132
|
+
recommendations: [
|
|
133
|
+
{
|
|
134
|
+
id: "R1",
|
|
135
|
+
title: "Primary Recommendation",
|
|
136
|
+
description: "Description of the recommendation.",
|
|
137
|
+
priority: "immediate",
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
|
|
141
|
+
targetState: {
|
|
142
|
+
description: "Replace with the vision description.",
|
|
143
|
+
keyCapabilities: ["Capability 1", "Capability 2", "Capability 3"],
|
|
144
|
+
successMetrics: ["Metric 1", "Metric 2", "Metric 3"],
|
|
145
|
+
},
|
|
146
|
+
|
|
147
|
+
roadmap: [
|
|
148
|
+
{
|
|
149
|
+
phase: "Phase 1",
|
|
150
|
+
title: "Foundation",
|
|
151
|
+
description: "Initial phase description.",
|
|
152
|
+
duration: "Weeks 1-4",
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
phase: "Phase 2",
|
|
156
|
+
title: "Implementation",
|
|
157
|
+
description: "Main implementation phase.",
|
|
158
|
+
duration: "Weeks 5-12",
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
phase: "Phase 3",
|
|
162
|
+
title: "Optimization",
|
|
163
|
+
description: "Optimization and scaling.",
|
|
164
|
+
duration: "Weeks 13-20",
|
|
165
|
+
},
|
|
166
|
+
],
|
|
167
|
+
|
|
168
|
+
callToAction: {
|
|
169
|
+
immediateSteps: ["Immediate next step 1", "Immediate next step 2"],
|
|
170
|
+
decisionPoints: [
|
|
171
|
+
"Decision point 1 requiring leadership approval",
|
|
172
|
+
"Decision point 2",
|
|
173
|
+
],
|
|
174
|
+
commitmentRequired: "Replace with the commitment required.",
|
|
175
|
+
},
|
|
176
|
+
};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "consulting-report",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"lint": "next lint"
|
|
9
|
+
},
|
|
10
|
+
"dependencies": {
|
|
11
|
+
"next": "16.2.4",
|
|
12
|
+
"react": "19.2.5",
|
|
13
|
+
"react-dom": "19.2.5",
|
|
14
|
+
"lucide-react": "1.14.0",
|
|
15
|
+
"clsx": "2.1.1",
|
|
16
|
+
"tailwind-merge": "3.5.0"
|
|
17
|
+
},
|
|
18
|
+
"devDependencies": {
|
|
19
|
+
"@tailwindcss/postcss": "4.2.4",
|
|
20
|
+
"@types/node": "25.6.0",
|
|
21
|
+
"@types/react": "19.2.14",
|
|
22
|
+
"@types/react-dom": "19.2.3",
|
|
23
|
+
"postcss": "8.5.13",
|
|
24
|
+
"tailwindcss": "4.2.4",
|
|
25
|
+
"typescript": "6.0.3"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"lib": ["dom", "dom.iterable", "esnext"],
|
|
4
|
+
"target": "ES2017",
|
|
5
|
+
"allowJs": true,
|
|
6
|
+
"skipLibCheck": true,
|
|
7
|
+
"strict": true,
|
|
8
|
+
"noEmit": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"module": "esnext",
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"resolveJsonModule": true,
|
|
13
|
+
"isolatedModules": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
"incremental": true,
|
|
16
|
+
"plugins": [{ "name": "next" }],
|
|
17
|
+
"paths": { "@/*": ["./*"] }
|
|
18
|
+
},
|
|
19
|
+
"include": [
|
|
20
|
+
"next-env.d.ts",
|
|
21
|
+
"**/*.ts",
|
|
22
|
+
"**/*.tsx",
|
|
23
|
+
".next/types/**/*.ts",
|
|
24
|
+
".next/dev/types/**/*.ts"
|
|
25
|
+
],
|
|
26
|
+
"exclude": ["node_modules"]
|
|
27
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
// consulting-report skill tool: launch the Next.js dev server for live preview.
|
|
4
|
+
//
|
|
5
|
+
// Usage:
|
|
6
|
+
// bun ~/.pal/skills/consulting-report/tools/dev.ts <report-dir>
|
|
7
|
+
|
|
8
|
+
import { spawnSync } from "node:child_process";
|
|
9
|
+
import { constants as fsConstants } from "node:fs";
|
|
10
|
+
import { access } from "node:fs/promises";
|
|
11
|
+
import { join, resolve } from "node:path";
|
|
12
|
+
|
|
13
|
+
async function exists(p: string): Promise<boolean> {
|
|
14
|
+
try {
|
|
15
|
+
await access(p, fsConstants.F_OK);
|
|
16
|
+
return true;
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function dev(reportDir: string): Promise<number> {
|
|
23
|
+
const dir = resolve(reportDir);
|
|
24
|
+
const pkg = join(dir, "package.json");
|
|
25
|
+
if (!(await exists(pkg))) {
|
|
26
|
+
throw new Error(`not a scaffolded report (missing package.json): ${dir}`);
|
|
27
|
+
}
|
|
28
|
+
const result = spawnSync("bun", ["run", "dev"], {
|
|
29
|
+
cwd: dir,
|
|
30
|
+
stdio: "inherit",
|
|
31
|
+
shell: true,
|
|
32
|
+
});
|
|
33
|
+
return result.status ?? 1;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function run(argv: string[] = process.argv.slice(2)): Promise<void> {
|
|
37
|
+
if (argv.length === 0) {
|
|
38
|
+
console.error("usage: dev.ts <report-dir>");
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
const status = await dev(argv[0]);
|
|
42
|
+
process.exit(status);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (import.meta.main) {
|
|
46
|
+
await run();
|
|
47
|
+
}
|