@pkprosol/coach 1.0.7 → 1.0.8
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/dist/bin/coach.js +65 -14
- package/dist/src/analyzer.d.ts +2 -1
- package/dist/src/analyzer.js +79 -0
- package/dist/src/display.d.ts +3 -1
- package/dist/src/display.js +60 -8
- package/dist/src/types.d.ts +12 -0
- package/package.json +1 -1
package/dist/bin/coach.js
CHANGED
|
@@ -3,8 +3,8 @@ import { createInterface } from "node:readline";
|
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import chalk from "chalk";
|
|
5
5
|
import { collectToday } from "../src/collector.js";
|
|
6
|
-
import { analyze, runClaude, buildHandoffPrompt, buildFocusPrompt, buildCostsPrompt, estimateCosts } from "../src/analyzer.js";
|
|
7
|
-
import { renderInsight, renderStreak, renderHistory, renderNoData, renderError, renderHandoff, renderFocus, renderRecap, renderGoals, renderCompare, renderWelcome, renderCosts, } from "../src/display.js";
|
|
6
|
+
import { analyze, runClaude, buildHandoffPrompt, buildFocusPrompt, buildCostsPrompt, buildStrategizePrompt, estimateCosts } from "../src/analyzer.js";
|
|
7
|
+
import { renderInsight, renderStreak, renderHistory, renderNoData, renderError, renderHandoff, renderFocus, renderRecap, renderGoals, renderCompare, renderWelcome, renderCosts, renderStrategize, renderCostNote, } from "../src/display.js";
|
|
8
8
|
import { isFirstRun, loadState, saveState, loadInsights, appendInsight, updateLastInsightRating, updateStreak, recordDimension, addGoal, completeGoal, clearCompletedGoals, recordDailyStat, } from "../src/storage.js";
|
|
9
9
|
function askQuestion(prompt) {
|
|
10
10
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -61,6 +61,12 @@ async function handleDefault() {
|
|
|
61
61
|
// Display
|
|
62
62
|
console.log("");
|
|
63
63
|
console.log(renderInsight(insight, state));
|
|
64
|
+
// Cost note
|
|
65
|
+
const costs = estimateCosts(data);
|
|
66
|
+
const totalCost = costs.reduce((s, c) => s + c.totalCost, 0);
|
|
67
|
+
if (totalCost > 0 || data.totalTokens > 0) {
|
|
68
|
+
console.log(renderCostNote(totalCost, data.sessions.length, data.totalTokens));
|
|
69
|
+
}
|
|
64
70
|
console.log("");
|
|
65
71
|
// Save insight (without rating yet)
|
|
66
72
|
const storedInsight = {
|
|
@@ -297,24 +303,66 @@ function handleCompare() {
|
|
|
297
303
|
console.log(renderCompare(todayStat, avg));
|
|
298
304
|
console.log("");
|
|
299
305
|
}
|
|
306
|
+
async function handleStrategize() {
|
|
307
|
+
const spinner = ora({ text: "Collecting recent sessions...", color: "cyan" }).start();
|
|
308
|
+
// Collect data for the last 5 days
|
|
309
|
+
const recentDays = [];
|
|
310
|
+
for (let i = 0; i < 5; i++) {
|
|
311
|
+
const date = new Date();
|
|
312
|
+
date.setDate(date.getDate() - i);
|
|
313
|
+
const dateStr = `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, "0")}-${String(date.getDate()).padStart(2, "0")}`;
|
|
314
|
+
const dayData = collectToday(dateStr);
|
|
315
|
+
if (dayData.prompts.length > 0) {
|
|
316
|
+
recentDays.push(dayData);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
if (recentDays.length === 0) {
|
|
320
|
+
spinner.stop();
|
|
321
|
+
console.log(renderNoData());
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
spinner.text = `Found data across ${recentDays.length} day${recentDays.length !== 1 ? "s" : ""}. Building strategy...`;
|
|
325
|
+
const state = loadState();
|
|
326
|
+
const pastInsights = loadInsights();
|
|
327
|
+
try {
|
|
328
|
+
const prompt = buildStrategizePrompt(recentDays, state.dailyStats, pastInsights, state.goals);
|
|
329
|
+
const text = await runClaude(prompt);
|
|
330
|
+
spinner.stop();
|
|
331
|
+
const cleaned = text.replace(/^```json?\s*/, "").replace(/\s*```$/, "").trim();
|
|
332
|
+
const analysis = JSON.parse(cleaned);
|
|
333
|
+
console.log("");
|
|
334
|
+
console.log(renderStrategize(analysis));
|
|
335
|
+
console.log("");
|
|
336
|
+
}
|
|
337
|
+
catch (err) {
|
|
338
|
+
spinner.stop();
|
|
339
|
+
if (err.message?.includes("claude CLI not found")) {
|
|
340
|
+
console.log(renderError("claude CLI not found. Install Claude Code first: https://docs.anthropic.com/en/docs/claude-code"));
|
|
341
|
+
}
|
|
342
|
+
else {
|
|
343
|
+
console.log(renderError(err.message ?? "Strategy analysis failed."));
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
300
347
|
function handleHelp() {
|
|
301
348
|
console.log(`
|
|
302
349
|
${chalk.bold("coach")} — Daily AI Work Coach
|
|
303
350
|
|
|
304
351
|
${chalk.bold("Usage:")}
|
|
305
|
-
coach
|
|
306
|
-
coach
|
|
307
|
-
coach
|
|
308
|
-
coach
|
|
309
|
-
coach
|
|
310
|
-
coach
|
|
311
|
-
coach goals
|
|
312
|
-
coach goals
|
|
352
|
+
coach Today's lesson + tip (default)
|
|
353
|
+
coach strategize Plan tomorrow based on recent work patterns
|
|
354
|
+
coach handoff Generate a handoff note for your current work
|
|
355
|
+
coach focus Analyze context-switching and focus patterns
|
|
356
|
+
coach costs Token costs, prompt engineering tips & LLM insights
|
|
357
|
+
coach recap Quick summary of today's stats (no AI)
|
|
358
|
+
coach goals Show current goals
|
|
359
|
+
coach goals set Add a goal: coach goals set "finish auth"
|
|
360
|
+
coach goals done Mark complete: coach goals done 1
|
|
313
361
|
coach goals clear Clear completed goals
|
|
314
|
-
coach compare
|
|
315
|
-
coach history
|
|
316
|
-
coach streak
|
|
317
|
-
coach help
|
|
362
|
+
coach compare Compare today vs recent averages
|
|
363
|
+
coach history Browse past insights
|
|
364
|
+
coach streak Show current streak + stats
|
|
365
|
+
coach help Show this help message
|
|
318
366
|
`);
|
|
319
367
|
}
|
|
320
368
|
// --- Main ---
|
|
@@ -339,6 +387,9 @@ async function main() {
|
|
|
339
387
|
case "costs":
|
|
340
388
|
await handleCosts();
|
|
341
389
|
break;
|
|
390
|
+
case "strategize":
|
|
391
|
+
await handleStrategize();
|
|
392
|
+
break;
|
|
342
393
|
case "recap":
|
|
343
394
|
handleRecap();
|
|
344
395
|
break;
|
package/dist/src/analyzer.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import type { CollectedData, CostEstimate, Dimension, Insight, StoredInsight } from "./types.js";
|
|
1
|
+
import type { CollectedData, CostEstimate, Dimension, Insight, StoredInsight, DailyStat, Goal } from "./types.js";
|
|
2
2
|
export declare function estimateCosts(data: CollectedData): CostEstimate[];
|
|
3
3
|
export declare function runClaude(prompt: string): Promise<string>;
|
|
4
4
|
export declare function buildHandoffPrompt(data: CollectedData): string;
|
|
5
5
|
export declare function buildFocusPrompt(data: CollectedData): string;
|
|
6
6
|
export declare function buildCostsPrompt(data: CollectedData, costs: CostEstimate[]): string;
|
|
7
|
+
export declare function buildStrategizePrompt(recentDays: CollectedData[], dailyStats: DailyStat[], pastInsights: StoredInsight[], goals: Goal[]): string;
|
|
7
8
|
export declare function analyze(data: CollectedData, recentDimensions: Dimension[], pastInsights: StoredInsight[]): Promise<Insight>;
|
package/dist/src/analyzer.js
CHANGED
|
@@ -333,6 +333,85 @@ Guidelines:
|
|
|
333
333
|
- Keep costs in perspective — compare to a cup of coffee, a SaaS subscription, etc.
|
|
334
334
|
- If tool calls are a significant portion of the work, explain how tool use affects costs (each tool result is input tokens on the next turn).
|
|
335
335
|
|
|
336
|
+
Respond with ONLY the JSON object, no markdown fences or other text.`;
|
|
337
|
+
}
|
|
338
|
+
export function buildStrategizePrompt(recentDays, dailyStats, pastInsights, goals) {
|
|
339
|
+
const daySummaries = recentDays.map((day) => {
|
|
340
|
+
const costs = estimateCosts(day);
|
|
341
|
+
const totalCost = costs.reduce((s, c) => s + c.totalCost, 0);
|
|
342
|
+
return {
|
|
343
|
+
date: day.date,
|
|
344
|
+
projects: day.projectsWorkedOn,
|
|
345
|
+
sessions: day.sessions.length,
|
|
346
|
+
prompts: day.prompts.length,
|
|
347
|
+
tokens: day.totalTokens,
|
|
348
|
+
toolCalls: day.totalToolCalls,
|
|
349
|
+
estimatedCost: `$${totalCost.toFixed(4)}`,
|
|
350
|
+
samplePrompts: day.prompts.slice(0, 15).map((p) => ({
|
|
351
|
+
text: p.text.slice(0, 300),
|
|
352
|
+
project: p.project,
|
|
353
|
+
})),
|
|
354
|
+
sessionDetails: day.sessions.map((s) => ({
|
|
355
|
+
project: s.project,
|
|
356
|
+
branch: s.gitBranch,
|
|
357
|
+
messages: s.messageCount,
|
|
358
|
+
toolCalls: s.toolCallCount,
|
|
359
|
+
tools: s.toolNames,
|
|
360
|
+
duration: s.startTime && s.endTime
|
|
361
|
+
? `${Math.round((new Date(s.endTime).getTime() - new Date(s.startTime).getTime()) / 60000)}min`
|
|
362
|
+
: "unknown",
|
|
363
|
+
})),
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
const recentInsights = pastInsights.slice(-7).map((i) => ({
|
|
367
|
+
date: i.date,
|
|
368
|
+
dimension: i.dimension,
|
|
369
|
+
lesson: i.lesson.slice(0, 200),
|
|
370
|
+
tip: i.tip.slice(0, 200),
|
|
371
|
+
rating: i.rating,
|
|
372
|
+
}));
|
|
373
|
+
const activeGoals = goals.filter((g) => !g.completedDate);
|
|
374
|
+
return `You are Coach, a strategic planning assistant. Analyze this developer's recent days of Claude Code usage to recommend where their time is best spent tomorrow.
|
|
375
|
+
|
|
376
|
+
## Recent Days of Work
|
|
377
|
+
${JSON.stringify(daySummaries, null, 2)}
|
|
378
|
+
|
|
379
|
+
## Daily Stats Trend (last ${dailyStats.length} days)
|
|
380
|
+
${JSON.stringify(dailyStats, null, 2)}
|
|
381
|
+
|
|
382
|
+
## Recent Coaching Insights
|
|
383
|
+
${JSON.stringify(recentInsights, null, 2)}
|
|
384
|
+
|
|
385
|
+
${activeGoals.length > 0 ? `## Active Goals
|
|
386
|
+
${activeGoals.map((g) => `- #${g.id}: ${g.text} (set ${g.createdDate})`).join("\n")}` : ""}
|
|
387
|
+
|
|
388
|
+
## Your Task
|
|
389
|
+
|
|
390
|
+
Analyze the patterns across multiple days — what projects are getting attention, what's been neglected, where momentum is building, where they might be stuck — and return a JSON object:
|
|
391
|
+
|
|
392
|
+
{
|
|
393
|
+
"recentPatterns": "2-3 sentences summarizing what you see across the recent days. What projects are hot? Any context-switching patterns? Is effort concentrated or scattered?",
|
|
394
|
+
"highImpactAreas": [
|
|
395
|
+
{
|
|
396
|
+
"area": "Project or task name",
|
|
397
|
+
"why": "Why this is the highest-impact use of time tomorrow (1-2 sentences)",
|
|
398
|
+
"suggestedAction": "Specific action to take (1 sentence)"
|
|
399
|
+
}
|
|
400
|
+
],
|
|
401
|
+
"tomorrowPlan": ["Ordered list of 3-5 concrete things to do tomorrow, phrased as actions"],
|
|
402
|
+
"avoidTomorrow": "One thing to consciously avoid or deprioritize tomorrow and why (1-2 sentences)",
|
|
403
|
+
"motivationalNote": "One genuine, grounded sentence connecting their recent work to a bigger picture"
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
Guidelines:
|
|
407
|
+
- highImpactAreas should have 2-4 items, ranked by impact
|
|
408
|
+
- Reference actual project names, branches, and patterns from the data
|
|
409
|
+
- The tomorrowPlan should be concrete and actionable, not generic
|
|
410
|
+
- If they have active goals, factor those into your recommendations
|
|
411
|
+
- If a project seems stuck (lots of prompts, little progress), call it out
|
|
412
|
+
- If a project has momentum (recent commits, clear direction), suggest riding that wave
|
|
413
|
+
- Be specific. "Continue working on auth" is too vague. "Finish the JWT refresh token flow in coach, then write integration tests" is better.
|
|
414
|
+
|
|
336
415
|
Respond with ONLY the JSON object, no markdown fences or other text.`;
|
|
337
416
|
}
|
|
338
417
|
export async function analyze(data, recentDimensions, pastInsights) {
|
package/dist/src/display.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Insight, CoachState, StoredInsight, Goal, CollectedData, DailyStat, CostAnalysis } from "./types.js";
|
|
1
|
+
import type { Insight, CoachState, StoredInsight, Goal, CollectedData, DailyStat, CostAnalysis, StrategizeAnalysis } from "./types.js";
|
|
2
2
|
export declare function renderInsight(insight: Insight, state: CoachState): string;
|
|
3
3
|
export declare function renderStreak(state: CoachState): string;
|
|
4
4
|
export declare function renderHistory(insights: StoredInsight[]): string;
|
|
@@ -23,3 +23,5 @@ export declare function renderRecap(data: CollectedData): string;
|
|
|
23
23
|
export declare function renderGoals(goals: Goal[]): string;
|
|
24
24
|
export declare function renderCompare(today: DailyStat, avg: DailyStat): string;
|
|
25
25
|
export declare function renderCosts(analysis: CostAnalysis): string;
|
|
26
|
+
export declare function renderStrategize(analysis: StrategizeAnalysis): string;
|
|
27
|
+
export declare function renderCostNote(totalCost: number, sessions: number, tokens: number): string;
|
package/dist/src/display.js
CHANGED
|
@@ -142,14 +142,15 @@ export function renderWelcome() {
|
|
|
142
142
|
out.push(padLine(" 3. Get a personalized insight + tip"));
|
|
143
143
|
out.push(padLine(""));
|
|
144
144
|
out.push(padLine(chalk.bold(" Commands:")));
|
|
145
|
-
out.push(padLine(" " + chalk.cyan("coach") + "
|
|
146
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
147
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
148
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
149
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
150
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
151
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
152
|
-
out.push(padLine(" " + chalk.cyan("coach
|
|
145
|
+
out.push(padLine(" " + chalk.cyan("coach") + " Today's lesson + tip"));
|
|
146
|
+
out.push(padLine(" " + chalk.cyan("coach strategize") + " Plan tomorrow's focus"));
|
|
147
|
+
out.push(padLine(" " + chalk.cyan("coach handoff") + " Handoff note for your work"));
|
|
148
|
+
out.push(padLine(" " + chalk.cyan("coach focus") + " Focus & context-switching"));
|
|
149
|
+
out.push(padLine(" " + chalk.cyan("coach costs") + " Cost analysis & LLM tips"));
|
|
150
|
+
out.push(padLine(" " + chalk.cyan("coach recap") + " Quick stats (no AI)"));
|
|
151
|
+
out.push(padLine(" " + chalk.cyan("coach goals") + " Track your goals"));
|
|
152
|
+
out.push(padLine(" " + chalk.cyan("coach compare") + " Today vs recent averages"));
|
|
153
|
+
out.push(padLine(" " + chalk.cyan("coach help") + " All commands"));
|
|
153
154
|
out.push(padLine(""));
|
|
154
155
|
out.push(padLine(chalk.bold(" Requirements:")));
|
|
155
156
|
out.push(padLine(" " + chalk.dim("Claude Code CLI must be installed and")));
|
|
@@ -354,3 +355,54 @@ export function renderCosts(analysis) {
|
|
|
354
355
|
out.push(boxBot());
|
|
355
356
|
return out.join("\n");
|
|
356
357
|
}
|
|
358
|
+
// === Strategize ===
|
|
359
|
+
export function renderStrategize(analysis) {
|
|
360
|
+
const out = [];
|
|
361
|
+
out.push(boxTop());
|
|
362
|
+
out.push(padLine(chalk.bold.white(" STRATEGIZE — TOMORROW'S PLAN")));
|
|
363
|
+
out.push(boxMid());
|
|
364
|
+
// Recent patterns
|
|
365
|
+
out.push(...renderSection(" 📊 Recent Patterns", analysis.recentPatterns));
|
|
366
|
+
// High impact areas
|
|
367
|
+
if (analysis.highImpactAreas.length > 0) {
|
|
368
|
+
out.push(padLine(""));
|
|
369
|
+
out.push(padLine(chalk.bold(" 🎯 High-Impact Areas")));
|
|
370
|
+
for (let i = 0; i < analysis.highImpactAreas.length; i++) {
|
|
371
|
+
const area = analysis.highImpactAreas[i];
|
|
372
|
+
out.push(padLine(""));
|
|
373
|
+
out.push(padLine(` ${chalk.bold.cyan(`${i + 1}. ${area.area}`)}`));
|
|
374
|
+
for (const line of wrapText(area.why, WIDTH - 6)) {
|
|
375
|
+
out.push(padLine(" " + line));
|
|
376
|
+
}
|
|
377
|
+
for (const line of wrapText(`→ ${area.suggestedAction}`, WIDTH - 6)) {
|
|
378
|
+
out.push(padLine(" " + chalk.green(line)));
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// Tomorrow plan
|
|
383
|
+
if (analysis.tomorrowPlan.length > 0) {
|
|
384
|
+
out.push(padLine(""));
|
|
385
|
+
out.push(padLine(chalk.bold(" 📋 Tomorrow's Action Plan")));
|
|
386
|
+
for (let i = 0; i < analysis.tomorrowPlan.length; i++) {
|
|
387
|
+
for (const line of wrapText(`${i + 1}. ${analysis.tomorrowPlan[i]}`, WIDTH - 6)) {
|
|
388
|
+
out.push(padLine(" " + line));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// Avoid
|
|
393
|
+
out.push(...renderSection(" 🚫 Deprioritize", analysis.avoidTomorrow));
|
|
394
|
+
// Motivation
|
|
395
|
+
out.push(padLine(""));
|
|
396
|
+
out.push(padLine(" 🌱 " + chalk.italic(analysis.motivationalNote)));
|
|
397
|
+
out.push(padLine(""));
|
|
398
|
+
out.push(boxBot());
|
|
399
|
+
return out.join("\n");
|
|
400
|
+
}
|
|
401
|
+
// === Cost Note (inline for default command) ===
|
|
402
|
+
export function renderCostNote(totalCost, sessions, tokens) {
|
|
403
|
+
const costStr = totalCost < 0.01 ? "<$0.01" : `~$${totalCost.toFixed(2)}`;
|
|
404
|
+
const tokStr = tokens >= 1_000_000
|
|
405
|
+
? `${(tokens / 1_000_000).toFixed(1)}M`
|
|
406
|
+
: `${(tokens / 1000).toFixed(0)}k`;
|
|
407
|
+
return chalk.dim(` 💰 Today so far: ${costStr} across ${sessions} session${sessions !== 1 ? "s" : ""} (${tokStr} tokens). Run \`coach costs\` for details.`);
|
|
408
|
+
}
|
package/dist/src/types.d.ts
CHANGED
|
@@ -87,6 +87,18 @@ export interface CostAnalysis {
|
|
|
87
87
|
efficiencyTips: string[];
|
|
88
88
|
promptEngineeringInsight: string;
|
|
89
89
|
}
|
|
90
|
+
export interface StrategizeArea {
|
|
91
|
+
area: string;
|
|
92
|
+
why: string;
|
|
93
|
+
suggestedAction: string;
|
|
94
|
+
}
|
|
95
|
+
export interface StrategizeAnalysis {
|
|
96
|
+
recentPatterns: string;
|
|
97
|
+
highImpactAreas: StrategizeArea[];
|
|
98
|
+
tomorrowPlan: string[];
|
|
99
|
+
avoidTomorrow: string;
|
|
100
|
+
motivationalNote: string;
|
|
101
|
+
}
|
|
90
102
|
export interface HistoryEntry {
|
|
91
103
|
display: string;
|
|
92
104
|
pastedContents: Record<string, unknown>;
|