@mrclrchtr/supi-insights 4.7.0 → 4.8.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/node_modules/@mrclrchtr/supi-core/README.md +26 -34
- package/node_modules/@mrclrchtr/supi-core/package.json +4 -7
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +4 -6
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +0 -20
- package/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +7 -1
- package/node_modules/@mrclrchtr/supi-core/src/config.ts +0 -1
- package/node_modules/@mrclrchtr/supi-core/src/context.ts +1 -9
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +4 -6
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +54 -28
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-schema.ts +91 -125
- package/node_modules/@mrclrchtr/supi-core/src/settings.ts +10 -7
- package/package.json +2 -5
- package/src/generator.ts +44 -147
- package/src/html.ts +8 -55
- package/src/insight-schemas.ts +91 -0
- package/src/insights.ts +33 -32
- package/src/parser.ts +24 -5
- package/src/types.ts +4 -10
- package/node_modules/@mrclrchtr/supi-core/src/context/context-messages.ts +0 -119
- package/node_modules/@mrclrchtr/supi-core/src/progress-widget.ts +0 -189
- package/node_modules/@mrclrchtr/supi-core/src/settings/scoped-settings-list.ts +0 -373
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-action-menu.ts +0 -102
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +0 -15
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-submenus.ts +0 -141
- package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +0 -118
- package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +0 -3
- package/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +0 -192
- package/src/api.ts +0 -1
|
@@ -1,33 +1,36 @@
|
|
|
1
|
-
// supi-core settings domain —
|
|
2
|
-
|
|
3
|
-
export { registerSettingsCommand } from "./settings/settings-command.ts";
|
|
1
|
+
// supi-core settings domain — settings modules and fixed-config adapters.
|
|
4
2
|
export type {
|
|
3
|
+
SettingsActionRequest,
|
|
4
|
+
SettingsApplyResult,
|
|
5
5
|
SettingsCollectionDiagnostic,
|
|
6
6
|
SettingsCollectionResult,
|
|
7
|
+
SettingsContext,
|
|
7
8
|
SettingsContributionCollector,
|
|
9
|
+
SettingsModule,
|
|
8
10
|
SettingsScope,
|
|
9
|
-
|
|
11
|
+
SettingsSnapshot,
|
|
10
12
|
} from "./settings/settings-registry.ts";
|
|
11
13
|
export {
|
|
12
14
|
createSettingsContributionCollector,
|
|
13
15
|
isSettingsContributionCollector,
|
|
16
|
+
registerSettings,
|
|
14
17
|
SUPI_SETTINGS_COLLECT_EVENT,
|
|
15
18
|
} from "./settings/settings-registry.ts";
|
|
16
19
|
export type {
|
|
17
20
|
BoolField,
|
|
18
21
|
ConfigHelpers,
|
|
22
|
+
ConfigSettingsOptions,
|
|
19
23
|
CustomField,
|
|
20
|
-
DeclarativeSettingsOptions,
|
|
21
24
|
EnumField,
|
|
22
25
|
ModelPickerField,
|
|
23
26
|
ModelPickerStaticOption,
|
|
24
27
|
NumberField,
|
|
25
28
|
ScopedFieldValue,
|
|
29
|
+
SettingsAction,
|
|
26
30
|
SettingsField,
|
|
27
|
-
SettingsFieldAction,
|
|
28
31
|
SettingsPersistedChange,
|
|
29
32
|
StringField,
|
|
30
33
|
StringListField,
|
|
31
34
|
ValueSource,
|
|
32
35
|
} from "./settings/settings-schema.ts";
|
|
33
|
-
export {
|
|
36
|
+
export { defineConfigSettings } from "./settings/settings-schema.ts";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrclrchtr/supi-insights",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.8.0",
|
|
4
4
|
"description": "Historical Pi-session reports and shareable HTML",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
"README.md"
|
|
35
35
|
],
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"
|
|
38
|
-
"@mrclrchtr/supi-core": "4.7.0"
|
|
37
|
+
"@mrclrchtr/supi-core": "4.8.0"
|
|
39
38
|
},
|
|
40
39
|
"bundledDependencies": [
|
|
41
40
|
"@mrclrchtr/supi-core"
|
|
@@ -62,9 +61,7 @@
|
|
|
62
61
|
],
|
|
63
62
|
"image": "https://raw.githubusercontent.com/mrclrchtr/supi/main/packages/supi-insights/assets/social-preview.png"
|
|
64
63
|
},
|
|
65
|
-
"main": "src/api.ts",
|
|
66
64
|
"exports": {
|
|
67
|
-
"./api": "./src/api.ts",
|
|
68
65
|
"./extension": "./src/extension.ts",
|
|
69
66
|
"./package.json": "./package.json"
|
|
70
67
|
}
|
package/src/generator.ts
CHANGED
|
@@ -2,108 +2,29 @@
|
|
|
2
2
|
|
|
3
3
|
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
4
4
|
import { callWithJsonResponse } from "@mrclrchtr/supi-core/llm";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
INSIGHT_SCHEMAS,
|
|
7
|
+
type InsightResultMap,
|
|
8
|
+
type InsightSchemaMap,
|
|
9
|
+
} from "./insight-schemas.ts";
|
|
6
10
|
import type { AggregatedData, InsightResults, SessionFacets } from "./types.ts";
|
|
7
11
|
|
|
8
|
-
// ── TypeBox schemas for each insight section ───────────────────────────────
|
|
9
|
-
|
|
10
|
-
const ProjectAreasSchema = Type.Object({
|
|
11
|
-
areas: Type.Array(
|
|
12
|
-
Type.Object({
|
|
13
|
-
name: Type.String(),
|
|
14
|
-
sessionCount: Type.Number(),
|
|
15
|
-
description: Type.String(),
|
|
16
|
-
}),
|
|
17
|
-
),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const InteractionStyleSchema = Type.Object({
|
|
21
|
-
narrative: Type.String(),
|
|
22
|
-
keyPattern: Type.String(),
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
const WhatWorksSchema = Type.Object({
|
|
26
|
-
intro: Type.String(),
|
|
27
|
-
impressiveWorkflows: Type.Array(
|
|
28
|
-
Type.Object({
|
|
29
|
-
title: Type.String(),
|
|
30
|
-
description: Type.String(),
|
|
31
|
-
}),
|
|
32
|
-
),
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const FrictionAnalysisSchema = Type.Object({
|
|
36
|
-
intro: Type.String(),
|
|
37
|
-
categories: Type.Array(
|
|
38
|
-
Type.Object({
|
|
39
|
-
category: Type.String(),
|
|
40
|
-
description: Type.String(),
|
|
41
|
-
examples: Type.Array(Type.String()),
|
|
42
|
-
}),
|
|
43
|
-
),
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const SuggestionsSchema = Type.Object({
|
|
47
|
-
claudeMdAdditions: Type.Array(
|
|
48
|
-
Type.Object({
|
|
49
|
-
addition: Type.String(),
|
|
50
|
-
why: Type.String(),
|
|
51
|
-
promptScaffold: Type.String(),
|
|
52
|
-
}),
|
|
53
|
-
),
|
|
54
|
-
featuresToTry: Type.Array(
|
|
55
|
-
Type.Object({
|
|
56
|
-
feature: Type.String(),
|
|
57
|
-
oneLiner: Type.String(),
|
|
58
|
-
whyForYou: Type.String(),
|
|
59
|
-
exampleCode: Type.String(),
|
|
60
|
-
}),
|
|
61
|
-
),
|
|
62
|
-
usagePatterns: Type.Array(
|
|
63
|
-
Type.Object({
|
|
64
|
-
title: Type.String(),
|
|
65
|
-
suggestion: Type.String(),
|
|
66
|
-
detail: Type.String(),
|
|
67
|
-
copyablePrompt: Type.String(),
|
|
68
|
-
}),
|
|
69
|
-
),
|
|
70
|
-
});
|
|
71
|
-
|
|
72
|
-
const OnTheHorizonSchema = Type.Object({
|
|
73
|
-
intro: Type.String(),
|
|
74
|
-
opportunities: Type.Array(
|
|
75
|
-
Type.Object({
|
|
76
|
-
title: Type.String(),
|
|
77
|
-
whatsPossible: Type.String(),
|
|
78
|
-
howToTry: Type.String(),
|
|
79
|
-
copyablePrompt: Type.String(),
|
|
80
|
-
}),
|
|
81
|
-
),
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const FunEndingSchema = Type.Object({
|
|
85
|
-
headline: Type.String(),
|
|
86
|
-
detail: Type.String(),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const AtAGlanceSchema = Type.Object({
|
|
90
|
-
whatsWorking: Type.String(),
|
|
91
|
-
whatsHindering: Type.String(),
|
|
92
|
-
quickWins: Type.String(),
|
|
93
|
-
ambitiousWorkflows: Type.String(),
|
|
94
|
-
});
|
|
95
|
-
|
|
96
12
|
// ── Section definitions ──────────────────────────────────────────────────
|
|
97
13
|
|
|
98
|
-
type
|
|
99
|
-
|
|
14
|
+
type ParallelInsightSectionName = Exclude<keyof InsightResultMap, "atAGlance">;
|
|
15
|
+
|
|
16
|
+
type InsightSection<Name extends ParallelInsightSectionName> = {
|
|
17
|
+
name: Name;
|
|
100
18
|
prompt: string;
|
|
101
|
-
|
|
102
|
-
schema: ReturnType<typeof Type.Object<any>>;
|
|
19
|
+
schema: InsightSchemaMap[Name];
|
|
103
20
|
maxTokens: number;
|
|
104
21
|
};
|
|
105
22
|
|
|
106
|
-
|
|
23
|
+
type ParallelInsightSection = {
|
|
24
|
+
[Name in ParallelInsightSectionName]: InsightSection<Name>;
|
|
25
|
+
}[ParallelInsightSectionName];
|
|
26
|
+
|
|
27
|
+
const INSIGHT_SECTIONS = [
|
|
107
28
|
{
|
|
108
29
|
name: "projectAreas",
|
|
109
30
|
prompt: `Analyze this PI usage data and identify project areas.
|
|
@@ -116,7 +37,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
116
37
|
}
|
|
117
38
|
|
|
118
39
|
Include 4-5 areas.`,
|
|
119
|
-
schema:
|
|
40
|
+
schema: INSIGHT_SCHEMAS.projectAreas,
|
|
120
41
|
maxTokens: 4096,
|
|
121
42
|
},
|
|
122
43
|
{
|
|
@@ -128,7 +49,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
128
49
|
"narrative": "2-3 paragraphs analyzing HOW the user interacts with PI. Use second person 'you'. Describe patterns: iterate quickly vs detailed upfront specs? Interrupt often or let the agent run? Include specific examples. Use **bold** for key insights.",
|
|
129
50
|
"keyPattern": "One sentence summary of most distinctive interaction style"
|
|
130
51
|
}`,
|
|
131
|
-
schema:
|
|
52
|
+
schema: INSIGHT_SCHEMAS.interactionStyle,
|
|
132
53
|
maxTokens: 4096,
|
|
133
54
|
},
|
|
134
55
|
{
|
|
@@ -144,7 +65,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
144
65
|
}
|
|
145
66
|
|
|
146
67
|
Include 3 impressive workflows.`,
|
|
147
|
-
schema:
|
|
68
|
+
schema: INSIGHT_SCHEMAS.whatWorks,
|
|
148
69
|
maxTokens: 4096,
|
|
149
70
|
},
|
|
150
71
|
{
|
|
@@ -160,7 +81,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
160
81
|
}
|
|
161
82
|
|
|
162
83
|
Include 3 friction categories with 2 examples each.`,
|
|
163
|
-
schema:
|
|
84
|
+
schema: INSIGHT_SCHEMAS.frictionAnalysis,
|
|
164
85
|
maxTokens: 4096,
|
|
165
86
|
},
|
|
166
87
|
{
|
|
@@ -181,7 +102,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
181
102
|
}
|
|
182
103
|
|
|
183
104
|
IMPORTANT: PRIORITIZE instructions that appear MULTIPLE TIMES in the user data.`,
|
|
184
|
-
schema:
|
|
105
|
+
schema: INSIGHT_SCHEMAS.suggestions,
|
|
185
106
|
maxTokens: 4096,
|
|
186
107
|
},
|
|
187
108
|
{
|
|
@@ -197,7 +118,7 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
197
118
|
}
|
|
198
119
|
|
|
199
120
|
Include 3 opportunities. Think BIG - autonomous workflows, parallel agents, iterating against tests.`,
|
|
200
|
-
schema:
|
|
121
|
+
schema: INSIGHT_SCHEMAS.onTheHorizon,
|
|
201
122
|
maxTokens: 4096,
|
|
202
123
|
},
|
|
203
124
|
{
|
|
@@ -211,10 +132,10 @@ RESPOND WITH ONLY A VALID JSON OBJECT:
|
|
|
211
132
|
}
|
|
212
133
|
|
|
213
134
|
Find something genuinely interesting or amusing from the session summaries.`,
|
|
214
|
-
schema:
|
|
135
|
+
schema: INSIGHT_SCHEMAS.funEnding,
|
|
215
136
|
maxTokens: 2048,
|
|
216
137
|
},
|
|
217
|
-
];
|
|
138
|
+
] satisfies readonly ParallelInsightSection[];
|
|
218
139
|
|
|
219
140
|
// ── Public API ─────────────────────────────────────────────────────────────
|
|
220
141
|
|
|
@@ -232,13 +153,11 @@ export async function generateInsights(
|
|
|
232
153
|
|
|
233
154
|
const insights: InsightResults = {};
|
|
234
155
|
for (const { name, result } of results) {
|
|
235
|
-
if (result) {
|
|
236
|
-
insights[name] = result;
|
|
237
|
-
}
|
|
156
|
+
if (result) Object.assign(insights, { [name]: result });
|
|
238
157
|
}
|
|
239
158
|
|
|
240
159
|
// Generate at_a_glance sequentially (needs other sections)
|
|
241
|
-
const atAGlance = await generateAtAGlance(
|
|
160
|
+
const atAGlance = await generateAtAGlance(insights, dataContext, ctx);
|
|
242
161
|
if (atAGlance) {
|
|
243
162
|
insights.atAGlance = atAGlance;
|
|
244
163
|
}
|
|
@@ -248,11 +167,11 @@ export async function generateInsights(
|
|
|
248
167
|
|
|
249
168
|
// ── Section generators ────────────────────────────────────────────────────
|
|
250
169
|
|
|
251
|
-
async function generateSectionInsight(
|
|
252
|
-
section: InsightSection
|
|
170
|
+
async function generateSectionInsight<Name extends ParallelInsightSectionName>(
|
|
171
|
+
section: InsightSection<Name>,
|
|
253
172
|
dataContext: string,
|
|
254
173
|
ctx: ExtensionContext,
|
|
255
|
-
)
|
|
174
|
+
) {
|
|
256
175
|
const result = await callWithJsonResponse(
|
|
257
176
|
ctx,
|
|
258
177
|
{
|
|
@@ -268,55 +187,33 @@ async function generateSectionInsight(
|
|
|
268
187
|
}
|
|
269
188
|
|
|
270
189
|
async function generateAtAGlance(
|
|
271
|
-
_data: AggregatedData,
|
|
272
190
|
insights: InsightResults,
|
|
273
191
|
dataContext: string,
|
|
274
192
|
ctx: ExtensionContext,
|
|
275
|
-
): Promise<
|
|
193
|
+
): Promise<InsightResultMap["atAGlance"] | null> {
|
|
276
194
|
const projectAreasText =
|
|
277
|
-
(
|
|
278
|
-
|
|
279
|
-
areas?: Array<{ name: string; description: string }>;
|
|
280
|
-
}
|
|
281
|
-
)?.areas
|
|
282
|
-
?.map((a) => `- ${a.name}: ${a.description}`)
|
|
283
|
-
.join("\n") || "";
|
|
195
|
+
insights.projectAreas?.areas.map((area) => `- ${area.name}: ${area.description}`).join("\n") ??
|
|
196
|
+
"";
|
|
284
197
|
|
|
285
198
|
const bigWinsText =
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
290
|
-
)?.impressiveWorkflows
|
|
291
|
-
?.map((w) => `- ${w.title}: ${w.description}`)
|
|
292
|
-
.join("\n") || "";
|
|
199
|
+
insights.whatWorks?.impressiveWorkflows
|
|
200
|
+
.map((workflow) => `- ${workflow.title}: ${workflow.description}`)
|
|
201
|
+
.join("\n") ?? "";
|
|
293
202
|
|
|
294
203
|
const frictionText =
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
)?.categories
|
|
300
|
-
?.map((c) => `- ${c.category}: ${c.description}`)
|
|
301
|
-
.join("\n") || "";
|
|
204
|
+
insights.frictionAnalysis?.categories
|
|
205
|
+
.map((category) => `- ${category.category}: ${category.description}`)
|
|
206
|
+
.join("\n") ?? "";
|
|
302
207
|
|
|
303
208
|
const featuresText =
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
}
|
|
308
|
-
)?.featuresToTry
|
|
309
|
-
?.map((f) => `- ${f.feature}: ${f.oneLiner}`)
|
|
310
|
-
.join("\n") || "";
|
|
209
|
+
insights.suggestions?.featuresToTry
|
|
210
|
+
.map((feature) => `- ${feature.feature}: ${feature.oneLiner}`)
|
|
211
|
+
.join("\n") ?? "";
|
|
311
212
|
|
|
312
213
|
const horizonText =
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
}
|
|
317
|
-
)?.opportunities
|
|
318
|
-
?.map((o) => `- ${o.title}: ${o.whatsPossible}`)
|
|
319
|
-
.join("\n") || "";
|
|
214
|
+
insights.onTheHorizon?.opportunities
|
|
215
|
+
.map((opportunity) => `- ${opportunity.title}: ${opportunity.whatsPossible}`)
|
|
216
|
+
.join("\n") ?? "";
|
|
320
217
|
|
|
321
218
|
const prompt = `You're writing an "At a Glance" summary for a PI usage insights report.
|
|
322
219
|
|
|
@@ -365,7 +262,7 @@ ${horizonText}`;
|
|
|
365
262
|
maxTokens: 4096,
|
|
366
263
|
retries: 2,
|
|
367
264
|
},
|
|
368
|
-
|
|
265
|
+
INSIGHT_SCHEMAS.atAGlance,
|
|
369
266
|
);
|
|
370
267
|
|
|
371
268
|
return result?.parsed ?? null;
|
package/src/html.ts
CHANGED
|
@@ -26,14 +26,7 @@ function generateReportJs(hourCountsJson: string): string {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
function renderAtAGlanceHtml(insights: InsightResults): string {
|
|
29
|
-
const atAGlance = insights.atAGlance
|
|
30
|
-
| {
|
|
31
|
-
whatsWorking?: string;
|
|
32
|
-
whatsHindering?: string;
|
|
33
|
-
quickWins?: string;
|
|
34
|
-
ambitiousWorkflows?: string;
|
|
35
|
-
}
|
|
36
|
-
| undefined;
|
|
29
|
+
const atAGlance = insights.atAGlance;
|
|
37
30
|
|
|
38
31
|
if (!atAGlance) return "";
|
|
39
32
|
|
|
@@ -51,12 +44,7 @@ function renderAtAGlanceHtml(insights: InsightResults): string {
|
|
|
51
44
|
}
|
|
52
45
|
|
|
53
46
|
function renderProjectAreasHtml(insights: InsightResults): string {
|
|
54
|
-
const projectAreas =
|
|
55
|
-
(
|
|
56
|
-
insights.projectAreas as
|
|
57
|
-
| { areas?: Array<{ name: string; sessionCount: number; description: string }> }
|
|
58
|
-
| undefined
|
|
59
|
-
)?.areas ?? [];
|
|
47
|
+
const projectAreas = insights.projectAreas?.areas ?? [];
|
|
60
48
|
|
|
61
49
|
if (projectAreas.length === 0) return "";
|
|
62
50
|
|
|
@@ -81,9 +69,7 @@ function renderProjectAreasHtml(insights: InsightResults): string {
|
|
|
81
69
|
}
|
|
82
70
|
|
|
83
71
|
function renderInteractionHtml(insights: InsightResults): string {
|
|
84
|
-
const interactionStyle = insights.interactionStyle
|
|
85
|
-
| { narrative?: string; keyPattern?: string }
|
|
86
|
-
| undefined;
|
|
72
|
+
const interactionStyle = insights.interactionStyle;
|
|
87
73
|
|
|
88
74
|
if (!interactionStyle?.narrative) return "";
|
|
89
75
|
|
|
@@ -97,9 +83,7 @@ function renderInteractionHtml(insights: InsightResults): string {
|
|
|
97
83
|
}
|
|
98
84
|
|
|
99
85
|
function renderWhatWorksHtml(insights: InsightResults): string {
|
|
100
|
-
const whatWorks = insights.whatWorks
|
|
101
|
-
| { intro?: string; impressiveWorkflows?: Array<{ title: string; description: string }> }
|
|
102
|
-
| undefined;
|
|
86
|
+
const whatWorks = insights.whatWorks;
|
|
103
87
|
|
|
104
88
|
if (!whatWorks?.impressiveWorkflows?.length) return "";
|
|
105
89
|
|
|
@@ -122,12 +106,7 @@ function renderWhatWorksHtml(insights: InsightResults): string {
|
|
|
122
106
|
}
|
|
123
107
|
|
|
124
108
|
function renderFrictionHtml(insights: InsightResults): string {
|
|
125
|
-
const frictionAnalysis = insights.frictionAnalysis
|
|
126
|
-
| {
|
|
127
|
-
intro?: string;
|
|
128
|
-
categories?: Array<{ category: string; description: string; examples?: string[] }>;
|
|
129
|
-
}
|
|
130
|
-
| undefined;
|
|
109
|
+
const frictionAnalysis = insights.frictionAnalysis;
|
|
131
110
|
|
|
132
111
|
if (!frictionAnalysis?.categories?.length) return "";
|
|
133
112
|
|
|
@@ -151,23 +130,7 @@ function renderFrictionHtml(insights: InsightResults): string {
|
|
|
151
130
|
}
|
|
152
131
|
|
|
153
132
|
function renderSuggestionsHtml(insights: InsightResults): string {
|
|
154
|
-
const suggestions = insights.suggestions
|
|
155
|
-
| {
|
|
156
|
-
claudeMdAdditions?: Array<{ addition: string; why: string; promptScaffold?: string }>;
|
|
157
|
-
featuresToTry?: Array<{
|
|
158
|
-
feature: string;
|
|
159
|
-
oneLiner: string;
|
|
160
|
-
whyForYou: string;
|
|
161
|
-
exampleCode?: string;
|
|
162
|
-
}>;
|
|
163
|
-
usagePatterns?: Array<{
|
|
164
|
-
title: string;
|
|
165
|
-
suggestion: string;
|
|
166
|
-
detail?: string;
|
|
167
|
-
copyablePrompt?: string;
|
|
168
|
-
}>;
|
|
169
|
-
}
|
|
170
|
-
| undefined;
|
|
133
|
+
const suggestions = insights.suggestions;
|
|
171
134
|
|
|
172
135
|
if (!suggestions) return "";
|
|
173
136
|
|
|
@@ -237,17 +200,7 @@ function renderSuggestionsHtml(insights: InsightResults): string {
|
|
|
237
200
|
}
|
|
238
201
|
|
|
239
202
|
function renderHorizonHtml(insights: InsightResults): string {
|
|
240
|
-
const horizonData = insights.onTheHorizon
|
|
241
|
-
| {
|
|
242
|
-
intro?: string;
|
|
243
|
-
opportunities?: Array<{
|
|
244
|
-
title: string;
|
|
245
|
-
whatsPossible: string;
|
|
246
|
-
howToTry?: string;
|
|
247
|
-
copyablePrompt?: string;
|
|
248
|
-
}>;
|
|
249
|
-
}
|
|
250
|
-
| undefined;
|
|
203
|
+
const horizonData = insights.onTheHorizon;
|
|
251
204
|
|
|
252
205
|
if (!horizonData?.opportunities?.length) return "";
|
|
253
206
|
|
|
@@ -272,7 +225,7 @@ function renderHorizonHtml(insights: InsightResults): string {
|
|
|
272
225
|
}
|
|
273
226
|
|
|
274
227
|
function renderFunEndingHtml(insights: InsightResults): string {
|
|
275
|
-
const funEnding = insights.funEnding
|
|
228
|
+
const funEnding = insights.funEnding;
|
|
276
229
|
|
|
277
230
|
if (!funEnding?.headline) return "";
|
|
278
231
|
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { type Static, Type } from "typebox";
|
|
2
|
+
|
|
3
|
+
/** Validated result schemas for each generated insight section. */
|
|
4
|
+
export const INSIGHT_SCHEMAS = {
|
|
5
|
+
projectAreas: Type.Object({
|
|
6
|
+
areas: Type.Array(
|
|
7
|
+
Type.Object({
|
|
8
|
+
name: Type.String(),
|
|
9
|
+
sessionCount: Type.Number(),
|
|
10
|
+
description: Type.String(),
|
|
11
|
+
}),
|
|
12
|
+
),
|
|
13
|
+
}),
|
|
14
|
+
interactionStyle: Type.Object({
|
|
15
|
+
narrative: Type.String(),
|
|
16
|
+
keyPattern: Type.String(),
|
|
17
|
+
}),
|
|
18
|
+
whatWorks: Type.Object({
|
|
19
|
+
intro: Type.String(),
|
|
20
|
+
impressiveWorkflows: Type.Array(
|
|
21
|
+
Type.Object({
|
|
22
|
+
title: Type.String(),
|
|
23
|
+
description: Type.String(),
|
|
24
|
+
}),
|
|
25
|
+
),
|
|
26
|
+
}),
|
|
27
|
+
frictionAnalysis: Type.Object({
|
|
28
|
+
intro: Type.String(),
|
|
29
|
+
categories: Type.Array(
|
|
30
|
+
Type.Object({
|
|
31
|
+
category: Type.String(),
|
|
32
|
+
description: Type.String(),
|
|
33
|
+
examples: Type.Array(Type.String()),
|
|
34
|
+
}),
|
|
35
|
+
),
|
|
36
|
+
}),
|
|
37
|
+
suggestions: Type.Object({
|
|
38
|
+
claudeMdAdditions: Type.Array(
|
|
39
|
+
Type.Object({
|
|
40
|
+
addition: Type.String(),
|
|
41
|
+
why: Type.String(),
|
|
42
|
+
promptScaffold: Type.String(),
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
featuresToTry: Type.Array(
|
|
46
|
+
Type.Object({
|
|
47
|
+
feature: Type.String(),
|
|
48
|
+
oneLiner: Type.String(),
|
|
49
|
+
whyForYou: Type.String(),
|
|
50
|
+
exampleCode: Type.String(),
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
53
|
+
usagePatterns: Type.Array(
|
|
54
|
+
Type.Object({
|
|
55
|
+
title: Type.String(),
|
|
56
|
+
suggestion: Type.String(),
|
|
57
|
+
detail: Type.String(),
|
|
58
|
+
copyablePrompt: Type.String(),
|
|
59
|
+
}),
|
|
60
|
+
),
|
|
61
|
+
}),
|
|
62
|
+
onTheHorizon: Type.Object({
|
|
63
|
+
intro: Type.String(),
|
|
64
|
+
opportunities: Type.Array(
|
|
65
|
+
Type.Object({
|
|
66
|
+
title: Type.String(),
|
|
67
|
+
whatsPossible: Type.String(),
|
|
68
|
+
howToTry: Type.String(),
|
|
69
|
+
copyablePrompt: Type.String(),
|
|
70
|
+
}),
|
|
71
|
+
),
|
|
72
|
+
}),
|
|
73
|
+
atAGlance: Type.Object({
|
|
74
|
+
whatsWorking: Type.String(),
|
|
75
|
+
whatsHindering: Type.String(),
|
|
76
|
+
quickWins: Type.String(),
|
|
77
|
+
ambitiousWorkflows: Type.String(),
|
|
78
|
+
}),
|
|
79
|
+
funEnding: Type.Object({
|
|
80
|
+
headline: Type.String(),
|
|
81
|
+
detail: Type.String(),
|
|
82
|
+
}),
|
|
83
|
+
} as const;
|
|
84
|
+
|
|
85
|
+
/** Schema type map keyed by insight section name. */
|
|
86
|
+
export type InsightSchemaMap = typeof INSIGHT_SCHEMAS;
|
|
87
|
+
|
|
88
|
+
/** Result types derived from the schemas used to validate model output. */
|
|
89
|
+
export type InsightResultMap = {
|
|
90
|
+
-readonly [Name in keyof InsightSchemaMap]: Static<InsightSchemaMap[Name]>;
|
|
91
|
+
};
|
package/src/insights.ts
CHANGED
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
} from "@earendil-works/pi-coding-agent";
|
|
14
14
|
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
15
15
|
import { loadSupiConfig } from "@mrclrchtr/supi-core/config";
|
|
16
|
-
import {
|
|
16
|
+
import { defineConfigSettings, registerSettings } from "@mrclrchtr/supi-core/settings";
|
|
17
17
|
import { aggregateData } from "./aggregator.ts";
|
|
18
18
|
import {
|
|
19
19
|
loadCachedFacets,
|
|
@@ -77,35 +77,38 @@ function getConfig(cwd: string): InsightsConfig {
|
|
|
77
77
|
|
|
78
78
|
export default function insightsExtension(pi: ExtensionAPI) {
|
|
79
79
|
// Register config-backed settings for /supi-settings
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
{
|
|
91
|
-
kind: "boolean" as const,
|
|
92
|
-
key: "enabled",
|
|
93
|
-
label: "Enable insights",
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
kind: "number" as const,
|
|
97
|
-
key: "maxSessions",
|
|
98
|
-
label: "Max sessions to analyze",
|
|
99
|
-
values: ["50", "100", "200", "500"],
|
|
100
|
-
},
|
|
101
|
-
{
|
|
102
|
-
kind: "number" as const,
|
|
103
|
-
key: "maxFacets",
|
|
104
|
-
label: "Max facet extractions",
|
|
105
|
-
values: ["20", "50", "100"],
|
|
80
|
+
registerSettings(
|
|
81
|
+
pi,
|
|
82
|
+
defineConfigSettings({
|
|
83
|
+
id: "insights",
|
|
84
|
+
label: "Insights",
|
|
85
|
+
section: "insights",
|
|
86
|
+
defaults: {
|
|
87
|
+
enabled: true,
|
|
88
|
+
maxSessions: MAX_SESSIONS_TO_ANALYZE,
|
|
89
|
+
maxFacets: MAX_FACET_EXTRACTIONS,
|
|
106
90
|
},
|
|
107
|
-
|
|
108
|
-
|
|
91
|
+
fields: [
|
|
92
|
+
{
|
|
93
|
+
kind: "boolean" as const,
|
|
94
|
+
key: "enabled",
|
|
95
|
+
label: "Enable insights",
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
kind: "number" as const,
|
|
99
|
+
key: "maxSessions",
|
|
100
|
+
label: "Max sessions to analyze",
|
|
101
|
+
values: ["50", "100", "200", "500"],
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
kind: "number" as const,
|
|
105
|
+
key: "maxFacets",
|
|
106
|
+
label: "Max facet extractions",
|
|
107
|
+
values: ["20", "50", "100"],
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
109
112
|
|
|
110
113
|
// ── /supi-insights command ──────────────────────────────────
|
|
111
114
|
|
|
@@ -155,9 +158,7 @@ export default function insightsExtension(pi: ExtensionAPI) {
|
|
|
155
158
|
|
|
156
159
|
const header = `# PI Insights\n\n${stats}\n${report.data.dateRange.start} to ${report.data.dateRange.end}\n`;
|
|
157
160
|
|
|
158
|
-
const atAGlance = report.insights.atAGlance
|
|
159
|
-
| { whatsWorking?: string; quickWins?: string }
|
|
160
|
-
| undefined;
|
|
161
|
+
const atAGlance = report.insights.atAGlance;
|
|
161
162
|
|
|
162
163
|
const summaryText = atAGlance
|
|
163
164
|
? `## At a Glance\n\n${atAGlance.whatsWorking ? `**What's working:** ${atAGlance.whatsWorking}` : ""}\n\n${atAGlance.quickWins ? `**Quick wins:** ${atAGlance.quickWins}` : ""}`
|