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.
Files changed (25) hide show
  1. package/assets/skills/consulting-report/SKILL.md +40 -1
  2. package/assets/skills/consulting-report/template/app/globals.css +51 -362
  3. package/assets/skills/consulting-report/template/app/layout.tsx +2 -8
  4. package/assets/skills/consulting-report/template/components/band-badge.tsx +72 -0
  5. package/assets/skills/consulting-report/template/components/callout.tsx +5 -3
  6. package/assets/skills/consulting-report/template/components/comparison-table.tsx +13 -7
  7. package/assets/skills/consulting-report/template/components/configuration-table.tsx +87 -0
  8. package/assets/skills/consulting-report/template/components/cover-page.tsx +18 -6
  9. package/assets/skills/consulting-report/template/components/exhibit.tsx +7 -5
  10. package/assets/skills/consulting-report/template/components/finding-card.tsx +7 -5
  11. package/assets/skills/consulting-report/template/components/quote-block.tsx +13 -3
  12. package/assets/skills/consulting-report/template/components/recommendation-card.tsx +6 -4
  13. package/assets/skills/consulting-report/template/components/rubric-table.tsx +104 -0
  14. package/assets/skills/consulting-report/template/components/score-badge.tsx +51 -0
  15. package/assets/skills/consulting-report/template/components/scorecard.tsx +154 -0
  16. package/assets/skills/consulting-report/template/components/section.tsx +16 -1
  17. package/assets/skills/consulting-report/template/components/severity-badge.tsx +20 -5
  18. package/assets/skills/consulting-report/template/components/stat-grid.tsx +11 -5
  19. package/assets/skills/consulting-report/template/components/table-of-contents.tsx +17 -7
  20. package/assets/skills/consulting-report/template/components/template-block.tsx +20 -0
  21. package/assets/skills/consulting-report/template/components/timeline.tsx +25 -6
  22. package/assets/skills/consulting-report/template/components/tuning-log.tsx +87 -0
  23. package/assets/skills/consulting-report/template/lib/report-data.ts +26 -74
  24. package/assets/skills/consulting-report/template/lib/types.ts +190 -0
  25. 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/report-data";
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 className="timeline">
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 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>
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
+ &nbsp;
79
+ </td>
80
+ </tr>
81
+ );
82
+ return row;
83
+ })}
84
+ </tbody>
85
+ </table>
86
+ );
87
+ }
@@ -1,77 +1,29 @@
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
- }
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.38.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
- "arch-check": "bun klint/cli.ts",
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
  }