@pkprosol/coach 1.0.6 → 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 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 Today's lesson + tip (default)
306
- coach handoff Generate a handoff note for your current work
307
- coach focus Analyze context-switching and focus patterns
308
- coach costs Token costs, prompt engineering tips & LLM insights
309
- coach recap Quick summary of today's stats (no AI)
310
- coach goals Show current goals
311
- coach goals set Add a goal: coach goals set "finish auth"
312
- coach goals done Mark complete: coach goals done 1
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 Compare today vs recent averages
315
- coach history Browse past insights
316
- coach streak Show current streak + stats
317
- coach help Show this help message
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 ---
@@ -325,6 +373,8 @@ async function main() {
325
373
  console.log("");
326
374
  console.log(renderWelcome());
327
375
  console.log("");
376
+ // Save initial state so isFirstRun() returns false next time
377
+ saveState(loadState());
328
378
  return;
329
379
  }
330
380
  switch (command) {
@@ -337,6 +387,9 @@ async function main() {
337
387
  case "costs":
338
388
  await handleCosts();
339
389
  break;
390
+ case "strategize":
391
+ await handleStrategize();
392
+ break;
340
393
  case "recap":
341
394
  handleRecap();
342
395
  break;
@@ -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>;
@@ -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) {
@@ -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;
@@ -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") + " Today's lesson + tip"));
146
- out.push(padLine(" " + chalk.cyan("coach handoff") + " Handoff note for your work"));
147
- out.push(padLine(" " + chalk.cyan("coach focus") + " Focus & context-switching"));
148
- out.push(padLine(" " + chalk.cyan("coach costs") + " Cost analysis & LLM tips"));
149
- out.push(padLine(" " + chalk.cyan("coach recap") + " Quick stats (no AI)"));
150
- out.push(padLine(" " + chalk.cyan("coach goals") + " Track your goals"));
151
- out.push(padLine(" " + chalk.cyan("coach compare") + " Today vs recent averages"));
152
- out.push(padLine(" " + chalk.cyan("coach help") + " All commands"));
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
+ }
@@ -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>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pkprosol/coach",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Daily AI work coach — analyzes your Claude Code & Claude App sessions to deliver one lesson + one tip daily",
5
5
  "type": "module",
6
6
  "bin": {