portable-agent-layer 0.38.0 → 0.40.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 +40 -1
- package/assets/skills/consulting-report/template/app/globals.css +51 -362
- package/assets/skills/consulting-report/template/app/layout.tsx +2 -8
- package/assets/skills/consulting-report/template/components/band-badge.tsx +72 -0
- package/assets/skills/consulting-report/template/components/callout.tsx +5 -3
- package/assets/skills/consulting-report/template/components/comparison-table.tsx +13 -7
- package/assets/skills/consulting-report/template/components/configuration-table.tsx +87 -0
- package/assets/skills/consulting-report/template/components/cover-page.tsx +18 -6
- package/assets/skills/consulting-report/template/components/exhibit.tsx +7 -5
- package/assets/skills/consulting-report/template/components/finding-card.tsx +7 -5
- package/assets/skills/consulting-report/template/components/quote-block.tsx +13 -3
- package/assets/skills/consulting-report/template/components/recommendation-card.tsx +6 -4
- package/assets/skills/consulting-report/template/components/rubric-table.tsx +104 -0
- package/assets/skills/consulting-report/template/components/score-badge.tsx +51 -0
- package/assets/skills/consulting-report/template/components/scorecard.tsx +154 -0
- package/assets/skills/consulting-report/template/components/section.tsx +16 -1
- package/assets/skills/consulting-report/template/components/severity-badge.tsx +20 -5
- package/assets/skills/consulting-report/template/components/stat-grid.tsx +11 -5
- package/assets/skills/consulting-report/template/components/table-of-contents.tsx +17 -7
- package/assets/skills/consulting-report/template/components/template-block.tsx +20 -0
- package/assets/skills/consulting-report/template/components/timeline.tsx +25 -6
- package/assets/skills/consulting-report/template/components/tuning-log.tsx +87 -0
- package/assets/skills/consulting-report/template/lib/report-data.ts +26 -74
- package/assets/skills/consulting-report/template/lib/types.ts +190 -0
- package/package.json +4 -5
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
interface TemplateBlockProps {
|
|
2
|
+
label?: string;
|
|
3
|
+
children: React.ReactNode;
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function TemplateBlock({
|
|
7
|
+
label = "Copy this template for each opportunity",
|
|
8
|
+
children,
|
|
9
|
+
}: TemplateBlockProps) {
|
|
10
|
+
return (
|
|
11
|
+
<div className="border border-dashed border-border-emphasis rounded-lg p-4 my-6 bg-background-secondary break-inside-avoid">
|
|
12
|
+
<div className="font-sans text-[0.68rem] font-bold uppercase tracking-widest text-primary mb-3">
|
|
13
|
+
{label}
|
|
14
|
+
</div>
|
|
15
|
+
<pre className="font-mono text-[0.78rem] leading-relaxed text-foreground whitespace-pre-wrap break-words m-0 bg-transparent">
|
|
16
|
+
<code>{children}</code>
|
|
17
|
+
</pre>
|
|
18
|
+
</div>
|
|
19
|
+
);
|
|
20
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { TimelinePhase } from "@/lib/
|
|
1
|
+
import type { TimelinePhase } from "@/lib/types";
|
|
2
2
|
|
|
3
3
|
interface TimelineProps {
|
|
4
4
|
phases: TimelinePhase[];
|
|
@@ -6,12 +6,31 @@ interface TimelineProps {
|
|
|
6
6
|
|
|
7
7
|
export function Timeline({ phases }: TimelineProps) {
|
|
8
8
|
return (
|
|
9
|
-
<div
|
|
9
|
+
<div
|
|
10
|
+
className={[
|
|
11
|
+
"relative pl-8",
|
|
12
|
+
// Vertical gradient line down the left edge.
|
|
13
|
+
"before:content-[''] before:absolute before:left-2 before:top-0 before:bottom-0",
|
|
14
|
+
"before:w-[2px] before:bg-gradient-to-b before:from-primary before:to-accent",
|
|
15
|
+
].join(" ")}
|
|
16
|
+
>
|
|
10
17
|
{phases.map((phase) => (
|
|
11
|
-
<div
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
18
|
+
<div
|
|
19
|
+
key={`${phase.phase}-${phase.title}`}
|
|
20
|
+
className={[
|
|
21
|
+
"relative pb-6",
|
|
22
|
+
// Dot on the vertical line.
|
|
23
|
+
"before:content-[''] before:absolute before:left-[-1.8125rem] before:top-2",
|
|
24
|
+
"before:w-3 before:h-3 before:rounded-full before:bg-primary",
|
|
25
|
+
].join(" ")}
|
|
26
|
+
>
|
|
27
|
+
<div className="font-sans font-semibold text-primary text-sm uppercase tracking-widest">
|
|
28
|
+
{phase.phase}
|
|
29
|
+
</div>
|
|
30
|
+
<div className="font-heading font-semibold text-foreground mt-1">
|
|
31
|
+
{phase.title}
|
|
32
|
+
</div>
|
|
33
|
+
<div className="text-muted text-[0.9375rem] mt-1">{phase.description}</div>
|
|
15
34
|
<div className="text-xs text-primary font-medium mt-1">{phase.duration}</div>
|
|
16
35
|
</div>
|
|
17
36
|
))}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import type { TuningLogEntry } from "@/lib/types";
|
|
2
|
+
|
|
3
|
+
export type TuningLogColumn = {
|
|
4
|
+
/** Property of `TuningLogEntry` to read for this column. */
|
|
5
|
+
key: keyof TuningLogEntry;
|
|
6
|
+
/** Header text shown in the table. */
|
|
7
|
+
header: string;
|
|
8
|
+
/** Optional className applied to all body cells in this column. */
|
|
9
|
+
cellClassName?: string;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const defaultColumns: TuningLogColumn[] = [
|
|
13
|
+
{
|
|
14
|
+
key: "date",
|
|
15
|
+
header: "Date",
|
|
16
|
+
cellClassName: "font-sans text-[0.75rem] text-muted whitespace-nowrap",
|
|
17
|
+
},
|
|
18
|
+
{ key: "parameter", header: "Parameter" },
|
|
19
|
+
{ key: "oldValue", header: "Old value" },
|
|
20
|
+
{ key: "newValue", header: "New value" },
|
|
21
|
+
{ key: "rationale", header: "Rationale" },
|
|
22
|
+
{ key: "approver", header: "Approver" },
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
interface TuningLogProps {
|
|
26
|
+
entries: TuningLogEntry[];
|
|
27
|
+
/**
|
|
28
|
+
* Override the default 6-column layout. Provide your own columns to add /
|
|
29
|
+
* remove / reorder fields. Defaults to:
|
|
30
|
+
* Date, Parameter, Old value, New value, Rationale, Approver.
|
|
31
|
+
*/
|
|
32
|
+
columns?: TuningLogColumn[];
|
|
33
|
+
/**
|
|
34
|
+
* Number of empty rows to render after the entries (useful for printable
|
|
35
|
+
* forms where future entries are filled in by hand). Defaults to 4.
|
|
36
|
+
*/
|
|
37
|
+
emptyRows?: number;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function TuningLog({
|
|
41
|
+
entries,
|
|
42
|
+
columns = defaultColumns,
|
|
43
|
+
emptyRows = 4,
|
|
44
|
+
}: TuningLogProps) {
|
|
45
|
+
const thClass =
|
|
46
|
+
"font-sans text-[0.65rem] font-bold uppercase tracking-widest text-primary px-2 py-2 border-b-2 border-primary text-left bg-background-secondary";
|
|
47
|
+
const tdClass = "px-2 py-2 border-b border-border-subtle align-top";
|
|
48
|
+
|
|
49
|
+
return (
|
|
50
|
+
<table className="w-full border-collapse my-4 font-body text-[0.82rem] break-inside-avoid">
|
|
51
|
+
<thead>
|
|
52
|
+
<tr>
|
|
53
|
+
{columns.map((c) => (
|
|
54
|
+
<th key={String(c.key)} className={thClass}>
|
|
55
|
+
{c.header}
|
|
56
|
+
</th>
|
|
57
|
+
))}
|
|
58
|
+
</tr>
|
|
59
|
+
</thead>
|
|
60
|
+
<tbody>
|
|
61
|
+
{entries.map((entry) => (
|
|
62
|
+
<tr key={`${entry.date}-${entry.parameter}`}>
|
|
63
|
+
{columns.map((c) => (
|
|
64
|
+
<td key={String(c.key)} className={`${tdClass} ${c.cellClassName ?? ""}`}>
|
|
65
|
+
{entry[c.key]}
|
|
66
|
+
</td>
|
|
67
|
+
))}
|
|
68
|
+
</tr>
|
|
69
|
+
))}
|
|
70
|
+
{Array.from({ length: emptyRows }, (_, i) => {
|
|
71
|
+
const row = (
|
|
72
|
+
// biome-ignore lint/suspicious/noArrayIndexKey: static padding rows, order is fixed
|
|
73
|
+
<tr key={`empty-${i}`}>
|
|
74
|
+
<td
|
|
75
|
+
colSpan={columns.length}
|
|
76
|
+
className="h-6 bg-background-secondary border-b border-border-subtle"
|
|
77
|
+
>
|
|
78
|
+
|
|
79
|
+
</td>
|
|
80
|
+
</tr>
|
|
81
|
+
);
|
|
82
|
+
return row;
|
|
83
|
+
})}
|
|
84
|
+
</tbody>
|
|
85
|
+
</table>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
@@ -1,77 +1,29 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
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
|
-
}
|
|
1
|
+
// Default placeholder report data.
|
|
2
|
+
//
|
|
3
|
+
// Each scaffolded project should REPLACE the contents of this file with its
|
|
4
|
+
// own data. The shapes are defined in ./types.ts — only the data lives here.
|
|
5
|
+
//
|
|
6
|
+
// The scaffold tool substitutes [CLIENT NAME] and "Strategic Assessment &
|
|
7
|
+
// Transformation Roadmap" when --client and --title are provided.
|
|
8
|
+
//
|
|
9
|
+
// Re-export the types from ./types here as well, so reports that import from
|
|
10
|
+
// "@/lib/report-data" continue to work.
|
|
11
|
+
|
|
12
|
+
export type {
|
|
13
|
+
ConfigurationParameter,
|
|
14
|
+
Dimension,
|
|
15
|
+
Finding,
|
|
16
|
+
Recommendation,
|
|
17
|
+
ReportData,
|
|
18
|
+
RubricLevel,
|
|
19
|
+
Scorecard,
|
|
20
|
+
ScorecardGate,
|
|
21
|
+
ScorecardScore,
|
|
22
|
+
TimelinePhase,
|
|
23
|
+
TuningLogEntry,
|
|
24
|
+
} from "./types";
|
|
25
|
+
|
|
26
|
+
import type { ReportData } from "./types";
|
|
75
27
|
|
|
76
28
|
export const reportData: ReportData = {
|
|
77
29
|
clientName: "[CLIENT NAME]",
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
// Type definitions for the consulting-report template.
|
|
2
|
+
//
|
|
3
|
+
// This file is the contract between the template (primitives + types) and the
|
|
4
|
+
// project (the report instance that supplies the actual data). Projects import
|
|
5
|
+
// the types they need; they do NOT edit this file.
|
|
6
|
+
//
|
|
7
|
+
// Two families of types:
|
|
8
|
+
//
|
|
9
|
+
// 1. Strategic-assessment shapes — Finding, Recommendation, TimelinePhase,
|
|
10
|
+
// and the ReportData interface composed of them. These power the default
|
|
11
|
+
// scaffolded layout (executive summary, findings, recommendations, etc.).
|
|
12
|
+
//
|
|
13
|
+
// 2. Scoring-playbook shapes — RubricLevel, Dimension, Scorecard, etc. These
|
|
14
|
+
// power rubric-style deliverables (an opportunity scoring playbook is one
|
|
15
|
+
// example). Each is optional; projects use whichever apply.
|
|
16
|
+
//
|
|
17
|
+
// Adding a new section type? Add its interface here so all reports share the
|
|
18
|
+
// same vocabulary.
|
|
19
|
+
|
|
20
|
+
export interface Finding {
|
|
21
|
+
id: string;
|
|
22
|
+
title: string;
|
|
23
|
+
description: string;
|
|
24
|
+
evidence: string;
|
|
25
|
+
source: string;
|
|
26
|
+
severity: "critical" | "high" | "medium" | "low";
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface Recommendation {
|
|
30
|
+
id: string;
|
|
31
|
+
title: string;
|
|
32
|
+
description: string;
|
|
33
|
+
priority: "immediate" | "short-term" | "long-term";
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface TimelinePhase {
|
|
37
|
+
phase: string;
|
|
38
|
+
title: string;
|
|
39
|
+
description: string;
|
|
40
|
+
duration: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export interface ReportData {
|
|
44
|
+
clientName: string;
|
|
45
|
+
reportTitle: string;
|
|
46
|
+
reportDate: string;
|
|
47
|
+
classification: string;
|
|
48
|
+
consultancyName: string;
|
|
49
|
+
preTitle?: string;
|
|
50
|
+
|
|
51
|
+
executiveSummary: {
|
|
52
|
+
context: string;
|
|
53
|
+
methodology: { interviewCount: number; roles: string[] };
|
|
54
|
+
keyFindings: string[];
|
|
55
|
+
primaryRecommendation: string;
|
|
56
|
+
expectedOutcomes: string[];
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
situationAssessment: {
|
|
60
|
+
currentState: string;
|
|
61
|
+
clientAsk: string;
|
|
62
|
+
whyNow: string;
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
findings: Finding[];
|
|
66
|
+
|
|
67
|
+
riskAnalysis: {
|
|
68
|
+
existentialRisks: string[];
|
|
69
|
+
competitiveThreats: string[];
|
|
70
|
+
timelinePressures: string;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
strategicOpportunity: {
|
|
74
|
+
goodNews: string;
|
|
75
|
+
requirements: string[];
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
recommendations: Recommendation[];
|
|
79
|
+
|
|
80
|
+
targetState: {
|
|
81
|
+
description: string;
|
|
82
|
+
keyCapabilities: string[];
|
|
83
|
+
successMetrics: string[];
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
roadmap: TimelinePhase[];
|
|
87
|
+
|
|
88
|
+
callToAction: {
|
|
89
|
+
immediateSteps: string[];
|
|
90
|
+
decisionPoints: string[];
|
|
91
|
+
commitmentRequired: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// --- Scoring-playbook shapes ---
|
|
96
|
+
|
|
97
|
+
export interface RubricLevel {
|
|
98
|
+
/** Numeric label for this level (typically 1..N, but free-form). */
|
|
99
|
+
score: number;
|
|
100
|
+
label: string;
|
|
101
|
+
anchor: string;
|
|
102
|
+
/**
|
|
103
|
+
* If true, the row renders with a saturated-color border to draw attention
|
|
104
|
+
* (e.g., flagging a veto-trigger score).
|
|
105
|
+
*/
|
|
106
|
+
highlight?: boolean;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export interface Dimension {
|
|
110
|
+
id: string;
|
|
111
|
+
name: string;
|
|
112
|
+
parenthetical?: string;
|
|
113
|
+
definition: string;
|
|
114
|
+
levels: RubricLevel[];
|
|
115
|
+
scoringGuidance?: string[];
|
|
116
|
+
/**
|
|
117
|
+
* Optional note about who tunes this dimension's anchors and when.
|
|
118
|
+
* Surfaced by the project's layout (often next to a "TUNABLE" marker).
|
|
119
|
+
*/
|
|
120
|
+
tuningNote?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export interface ScorecardGate {
|
|
124
|
+
name: string;
|
|
125
|
+
verdict: "pass" | "fail" | "n/a" | "flag";
|
|
126
|
+
note?: string;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
export interface ScorecardScore {
|
|
130
|
+
dimensionId: string;
|
|
131
|
+
dimensionLabel: string;
|
|
132
|
+
/** Score for this dimension. Typically 1..N matching the rubric. */
|
|
133
|
+
score: number;
|
|
134
|
+
justification: string;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface Scorecard {
|
|
138
|
+
id: string;
|
|
139
|
+
index: number;
|
|
140
|
+
name: string;
|
|
141
|
+
outcome: string;
|
|
142
|
+
inventoryRef?: string;
|
|
143
|
+
gates: ScorecardGate[];
|
|
144
|
+
scores: ScorecardScore[];
|
|
145
|
+
total: number;
|
|
146
|
+
/**
|
|
147
|
+
* Free-form band label. The default `BandBadge` style map covers
|
|
148
|
+
* "Strong" | "Promising" | "Park" | "Decline"; projects can use any
|
|
149
|
+
* string and supply their own style map.
|
|
150
|
+
*/
|
|
151
|
+
band: string;
|
|
152
|
+
/**
|
|
153
|
+
* Maximum possible score (used by Scorecard to render "total / maxTotal").
|
|
154
|
+
* Defaults to 20 when omitted.
|
|
155
|
+
*/
|
|
156
|
+
maxTotal?: number;
|
|
157
|
+
weightingNote?: string;
|
|
158
|
+
vetoTriggered: boolean;
|
|
159
|
+
vetoNote?: string;
|
|
160
|
+
recommendation: string;
|
|
161
|
+
firstAction: string;
|
|
162
|
+
owner: string;
|
|
163
|
+
nextReview: string;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
export interface ConfigurationParameter {
|
|
167
|
+
name: string;
|
|
168
|
+
currentValue: string;
|
|
169
|
+
location: string;
|
|
170
|
+
rationale?: string;
|
|
171
|
+
/**
|
|
172
|
+
* If true, this parameter is tunable by the report's owner. Renders a
|
|
173
|
+
* marker next to the parameter name.
|
|
174
|
+
*/
|
|
175
|
+
tunable?: boolean;
|
|
176
|
+
/**
|
|
177
|
+
* Optional label for the tunable marker (e.g., "Owner edit", "Tune at sync").
|
|
178
|
+
* Defaults to "TUNABLE" when omitted.
|
|
179
|
+
*/
|
|
180
|
+
tunableLabel?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
export interface TuningLogEntry {
|
|
184
|
+
date: string;
|
|
185
|
+
parameter: string;
|
|
186
|
+
oldValue: string;
|
|
187
|
+
newValue: string;
|
|
188
|
+
rationale: string;
|
|
189
|
+
approver: string;
|
|
190
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "portable-agent-layer",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.40.0",
|
|
4
4
|
"description": "PAL — Portable Agent Layer: persistent personal context for AI coding assistants",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -41,8 +41,7 @@
|
|
|
41
41
|
"check": "biome check",
|
|
42
42
|
"check-write": "biome check --write",
|
|
43
43
|
"knip": "knip-bun",
|
|
44
|
-
"
|
|
45
|
-
"klint:schema": "bun klint/tools/generate-schema.ts",
|
|
44
|
+
"klint": "klint",
|
|
46
45
|
"lint-staged": "lint-staged",
|
|
47
46
|
"prepare": "bun .husky/install.mjs",
|
|
48
47
|
"install:all": "bun run src/cli/index.ts cli install",
|
|
@@ -79,9 +78,9 @@
|
|
|
79
78
|
},
|
|
80
79
|
"dependencies": {
|
|
81
80
|
"@clack/prompts": "^1.3.0",
|
|
81
|
+
"@konvert7/klint": "^0.2.0",
|
|
82
82
|
"adm-zip": "^0.5.17",
|
|
83
83
|
"marked": "^15.0.12",
|
|
84
|
-
"playwright": "^1.59.1"
|
|
85
|
-
"zod": "^4.4.3"
|
|
84
|
+
"playwright": "^1.59.1"
|
|
86
85
|
}
|
|
87
86
|
}
|