@tekmidian/pai 0.9.5 → 0.9.7

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.
@@ -27,9 +27,9 @@ function getAdvisorGuidance() {
27
27
  let mode = config.mode ?? "auto";
28
28
  if (mode === "auto" && typeof config.weeklyBudgetPercent === "number") {
29
29
  const pct = config.weeklyBudgetPercent;
30
- if (pct < 70) mode = "normal";
31
- else if (pct < 85) mode = "conservative";
32
- else if (pct < 95) mode = "strict";
30
+ if (pct < 60) mode = "normal";
31
+ else if (pct < 80) mode = "conservative";
32
+ else if (pct < 92) mode = "strict";
33
33
  else mode = "critical";
34
34
  }
35
35
  if (config.forceModel) {
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/hooks/ts/user-prompt/whisper-rules.ts"],
4
- "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * whisper-rules.ts\n *\n * UserPromptSubmit hook that injects:\n * 1. User-defined whisper rules from ~/.claude/whisper-rules.md\n * 2. Budget-aware model tiering guidance from ~/.claude/advisor-mode.json\n *\n * The advisor mode implements the \"advisor strategy\" pattern:\n * - Normal (budget < 70%): use any model freely\n * - Conservative (70-85%): prefer haiku for subagents, sonnet for main work\n * - Strict (85-95%): haiku only for subagents, main context stays on current model\n * - Critical (>95%): minimize all subagent spawning, essential work only\n *\n * Budget percentage is written by the statusline or manually to advisor-mode.json.\n * If the file doesn't exist, no advisor guidance is injected.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst WHISPER_FILE = join(homedir(), \".claude\", \"whisper-rules.md\");\nconst ADVISOR_FILE = join(homedir(), \".claude\", \"advisor-mode.json\");\n\nfunction getWhisperRules(): string {\n if (existsSync(WHISPER_FILE)) {\n try {\n const content = readFileSync(WHISPER_FILE, \"utf-8\").trim();\n if (content) return content;\n } catch { /* ignore */ }\n }\n return \"\";\n}\n\ninterface AdvisorConfig {\n weeklyBudgetPercent?: number; // 0-100, written by statusline or manually\n mode?: \"normal\" | \"conservative\" | \"strict\" | \"critical\" | \"auto\";\n forceModel?: string; // override: always use this model for subagents\n}\n\nfunction getAdvisorGuidance(): string {\n if (!existsSync(ADVISOR_FILE)) return \"\";\n\n let config: AdvisorConfig;\n try {\n config = JSON.parse(readFileSync(ADVISOR_FILE, \"utf-8\"));\n } catch {\n return \"\";\n }\n\n // Determine mode\n let mode = config.mode ?? \"auto\";\n if (mode === \"auto\" && typeof config.weeklyBudgetPercent === \"number\") {\n const pct = config.weeklyBudgetPercent;\n if (pct < 70) mode = \"normal\";\n else if (pct < 85) mode = \"conservative\";\n else if (pct < 95) mode = \"strict\";\n else mode = \"critical\";\n }\n\n // Force model override\n if (config.forceModel) {\n return `ADVISOR MODE: Use model \"${config.forceModel}\" for ALL subagents (Agent tool calls). This is a manual override.`;\n }\n\n switch (mode) {\n case \"normal\":\n return \"\"; // No constraints \u2014 use models freely\n\n case \"conservative\":\n return [\n `ADVISOR MODE (conservative \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"Use HAIKU for all subagents (Agent tool, model: haiku) unless the task explicitly requires deep reasoning.\",\n \"Main context stays on the current model. Only escalate subagents to sonnet if haiku output quality is insufficient.\",\n \"Prefer fewer, more focused subagent calls over many parallel ones.\",\n ].join(\" \");\n\n case \"strict\":\n return [\n `ADVISOR MODE (strict \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"Use HAIKU for ALL subagents without exception (Agent tool, model: haiku).\",\n \"Minimize subagent spawning \u2014 do simple tasks directly in main context.\",\n \"Only spawn subagents for genuinely independent parallel work.\",\n \"Never spawn opus subagents.\",\n ].join(\" \");\n\n case \"critical\":\n return [\n `ADVISOR MODE (critical \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"MINIMIZE ALL TOKEN USAGE. Do NOT spawn subagents unless absolutely essential.\",\n \"Work directly in main context. Keep responses concise.\",\n \"Use haiku model if you must spawn a subagent.\",\n \"Skip background research, parallel exploration, and spotchecks.\",\n \"The user is near their weekly limit \u2014 every token counts.\",\n ].join(\" \");\n\n default:\n return \"\";\n }\n}\n\nfunction main() {\n const parts: string[] = [];\n\n const rules = getWhisperRules();\n if (rules) parts.push(rules);\n\n const advisor = getAdvisorGuidance();\n if (advisor) parts.push(advisor);\n\n if (parts.length === 0) return;\n\n console.log(`<system-reminder>\\n${parts.join(\"\\n\")}\\n</system-reminder>`);\n}\n\nmain();\n"],
4
+ "sourcesContent": ["#!/usr/bin/env node\n\n/**\n * whisper-rules.ts\n *\n * UserPromptSubmit hook that injects:\n * 1. User-defined whisper rules from ~/.claude/whisper-rules.md\n * 2. Budget-aware model tiering guidance from ~/.claude/advisor-mode.json\n *\n * The advisor mode implements the \"advisor strategy\" pattern:\n * - Normal (budget < 70%): use any model freely\n * - Conservative (70-85%): prefer haiku for subagents, sonnet for main work\n * - Strict (85-95%): haiku only for subagents, main context stays on current model\n * - Critical (>95%): minimize all subagent spawning, essential work only\n *\n * Budget percentage is written by the statusline or manually to advisor-mode.json.\n * If the file doesn't exist, no advisor guidance is injected.\n */\n\nimport { existsSync, readFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nconst WHISPER_FILE = join(homedir(), \".claude\", \"whisper-rules.md\");\nconst ADVISOR_FILE = join(homedir(), \".claude\", \"advisor-mode.json\");\n\nfunction getWhisperRules(): string {\n if (existsSync(WHISPER_FILE)) {\n try {\n const content = readFileSync(WHISPER_FILE, \"utf-8\").trim();\n if (content) return content;\n } catch { /* ignore */ }\n }\n return \"\";\n}\n\ninterface AdvisorConfig {\n weeklyBudgetPercent?: number; // 0-100, written by statusline or manually\n mode?: \"normal\" | \"conservative\" | \"strict\" | \"critical\" | \"auto\";\n forceModel?: string; // override: always use this model for subagents\n}\n\nfunction getAdvisorGuidance(): string {\n if (!existsSync(ADVISOR_FILE)) return \"\";\n\n let config: AdvisorConfig;\n try {\n config = JSON.parse(readFileSync(ADVISOR_FILE, \"utf-8\"));\n } catch {\n return \"\";\n }\n\n // Determine mode\n let mode = config.mode ?? \"auto\";\n if (mode === \"auto\" && typeof config.weeklyBudgetPercent === \"number\") {\n const pct = config.weeklyBudgetPercent;\n if (pct < 60) mode = \"normal\";\n else if (pct < 80) mode = \"conservative\";\n else if (pct < 92) mode = \"strict\";\n else mode = \"critical\";\n }\n\n // Force model override\n if (config.forceModel) {\n return `ADVISOR MODE: Use model \"${config.forceModel}\" for ALL subagents (Agent tool calls). This is a manual override.`;\n }\n\n switch (mode) {\n case \"normal\":\n return \"\"; // No constraints \u2014 use models freely\n\n case \"conservative\":\n return [\n `ADVISOR MODE (conservative \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"Use HAIKU for all subagents (Agent tool, model: haiku) unless the task explicitly requires deep reasoning.\",\n \"Main context stays on the current model. Only escalate subagents to sonnet if haiku output quality is insufficient.\",\n \"Prefer fewer, more focused subagent calls over many parallel ones.\",\n ].join(\" \");\n\n case \"strict\":\n return [\n `ADVISOR MODE (strict \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"Use HAIKU for ALL subagents without exception (Agent tool, model: haiku).\",\n \"Minimize subagent spawning \u2014 do simple tasks directly in main context.\",\n \"Only spawn subagents for genuinely independent parallel work.\",\n \"Never spawn opus subagents.\",\n ].join(\" \");\n\n case \"critical\":\n return [\n `ADVISOR MODE (critical \u2014 weekly budget at ${config.weeklyBudgetPercent ?? \"?\"}%):`,\n \"MINIMIZE ALL TOKEN USAGE. Do NOT spawn subagents unless absolutely essential.\",\n \"Work directly in main context. Keep responses concise.\",\n \"Use haiku model if you must spawn a subagent.\",\n \"Skip background research, parallel exploration, and spotchecks.\",\n \"The user is near their weekly limit \u2014 every token counts.\",\n ].join(\" \");\n\n default:\n return \"\";\n }\n}\n\nfunction main() {\n const parts: string[] = [];\n\n const rules = getWhisperRules();\n if (rules) parts.push(rules);\n\n const advisor = getAdvisorGuidance();\n if (advisor) parts.push(advisor);\n\n if (parts.length === 0) return;\n\n console.log(`<system-reminder>\\n${parts.join(\"\\n\")}\\n</system-reminder>`);\n}\n\nmain();\n"],
5
5
  "mappings": ";;;AAmBA,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,eAAe;AAExB,IAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,kBAAkB;AAClE,IAAM,eAAe,KAAK,QAAQ,GAAG,WAAW,mBAAmB;AAEnE,SAAS,kBAA0B;AACjC,MAAI,WAAW,YAAY,GAAG;AAC5B,QAAI;AACF,YAAM,UAAU,aAAa,cAAc,OAAO,EAAE,KAAK;AACzD,UAAI,QAAS,QAAO;AAAA,IACtB,QAAQ;AAAA,IAAe;AAAA,EACzB;AACA,SAAO;AACT;AAQA,SAAS,qBAA6B;AACpC,MAAI,CAAC,WAAW,YAAY,EAAG,QAAO;AAEtC,MAAI;AACJ,MAAI;AACF,aAAS,KAAK,MAAM,aAAa,cAAc,OAAO,CAAC;AAAA,EACzD,QAAQ;AACN,WAAO;AAAA,EACT;AAGA,MAAI,OAAO,OAAO,QAAQ;AAC1B,MAAI,SAAS,UAAU,OAAO,OAAO,wBAAwB,UAAU;AACrE,UAAM,MAAM,OAAO;AACnB,QAAI,MAAM,GAAI,QAAO;AAAA,aACZ,MAAM,GAAI,QAAO;AAAA,aACjB,MAAM,GAAI,QAAO;AAAA,QACrB,QAAO;AAAA,EACd;AAGA,MAAI,OAAO,YAAY;AACrB,WAAO,4BAA4B,OAAO,UAAU;AAAA,EACtD;AAEA,UAAQ,MAAM;AAAA,IACZ,KAAK;AACH,aAAO;AAAA;AAAA,IAET,KAAK;AACH,aAAO;AAAA,QACL,sDAAiD,OAAO,uBAAuB,GAAG;AAAA,QAClF;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,IAEZ,KAAK;AACH,aAAO;AAAA,QACL,gDAA2C,OAAO,uBAAuB,GAAG;AAAA,QAC5E;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,IAEZ,KAAK;AACH,aAAO;AAAA,QACL,kDAA6C,OAAO,uBAAuB,GAAG;AAAA,QAC9E;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,EAAE,KAAK,GAAG;AAAA,IAEZ;AACE,aAAO;AAAA,EACX;AACF;AAEA,SAAS,OAAO;AACd,QAAM,QAAkB,CAAC;AAEzB,QAAM,QAAQ,gBAAgB;AAC9B,MAAI,MAAO,OAAM,KAAK,KAAK;AAE3B,QAAM,UAAU,mBAAmB;AACnC,MAAI,QAAS,OAAM,KAAK,OAAO;AAE/B,MAAI,MAAM,WAAW,EAAG;AAExB,UAAQ,IAAI;AAAA,EAAsB,MAAM,KAAK,IAAI,CAAC;AAAA,mBAAsB;AAC1E;AAEA,KAAK;",
6
6
  "names": []
7
7
  }
@@ -7,6 +7,15 @@ description: "Manage budget-aware advisor mode — control model tiering for sub
7
7
 
8
8
  USE WHEN user says 'advisor', 'budget mode', 'set advisor', 'save budget', '/advisor', 'how much budget', OR wants to control model tiering for subagents.
9
9
 
10
+ ALSO USE WHEN user says plain-language budget/mode phrases like:
11
+ - "go easy on the budget", "save tokens", "be conservative" → set mode to conservative
12
+ - "use haiku only", "strict mode", "budget is tight" → set mode to strict
13
+ - "lock it down", "critical mode", "almost out of budget" → set mode to critical
14
+ - "go full power", "use whatever model", "no budget constraints", "normal mode", "unrestricted" → set mode to normal
15
+ - "back to auto", "reset advisor", "let the budget decide" → set mode to auto
16
+
17
+ When you detect these, write the appropriate mode to `~/.claude/advisor-mode.json` and confirm what you did.
18
+
10
19
  Advisor mode controls which models subagents use, based on weekly budget consumption.
11
20
 
12
21
  ### Configuration
@@ -27,15 +36,16 @@ The config file is at `~/.claude/advisor-mode.json`:
27
36
  - `/advisor auto` — reset to auto mode (derives from weeklyBudgetPercent)
28
37
  - `/advisor force <model>` — force all subagents to use a specific model (haiku/sonnet/opus)
29
38
  - `/advisor reset` — remove the config file (no advisor guidance injected)
39
+ - Or just say it in plain language — see triggers above
30
40
 
31
41
  ### Mode Thresholds (auto mode)
32
42
 
33
43
  | Budget Used | Mode | Subagent Model | Behavior |
34
44
  |-------------|------|----------------|----------|
35
- | < 70% | normal | Any | No constraints |
36
- | 70-85% | conservative | Haiku preferred | Escalate to sonnet only if haiku insufficient |
37
- | 85-95% | strict | Haiku only | Minimize spawning, no opus subagents |
38
- | > 95% | critical | Haiku or none | Essential work only, minimize all token usage |
45
+ | < 60% | normal | Any | No constraints |
46
+ | 60-80% | conservative | Haiku preferred | Escalate to sonnet only if haiku insufficient |
47
+ | 80-92% | strict | Haiku only | Minimize spawning, no opus subagents |
48
+ | > 92% | critical | Haiku or none | Essential work only, minimize all token usage |
39
49
 
40
50
  ### Workflow
41
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekmidian/pai",
3
- "version": "0.9.5",
3
+ "version": "0.9.7",
4
4
  "description": "PAI Knowledge OS — Personal AI Infrastructure with federated memory and project management",
5
5
  "type": "module",
6
6
  "main": "dist/index.mjs",
@@ -54,9 +54,9 @@ function getAdvisorGuidance(): string {
54
54
  let mode = config.mode ?? "auto";
55
55
  if (mode === "auto" && typeof config.weeklyBudgetPercent === "number") {
56
56
  const pct = config.weeklyBudgetPercent;
57
- if (pct < 70) mode = "normal";
58
- else if (pct < 85) mode = "conservative";
59
- else if (pct < 95) mode = "strict";
57
+ if (pct < 60) mode = "normal";
58
+ else if (pct < 80) mode = "conservative";
59
+ else if (pct < 92) mode = "strict";
60
60
  else mode = "critical";
61
61
  }
62
62
 
@@ -427,10 +427,54 @@ if [ -f "$usage_cache" ]; then
427
427
  pace_dot="${pace_color}${spend_per_day}%% / ${budget_per_day}%%${RESET}"
428
428
  fi
429
429
 
430
- # Build usage suffix: 5h: 8% 00:59 1d: ● 29% / 36% │ 7d: 29% → Fr. 08:00
430
+ # Write weekly budget to advisor-mode.json for the whisper hook
431
+ # Preserve existing mode if manually set — only update weeklyBudgetPercent
432
+ _advisor_file="${HOME}/.claude/advisor-mode.json"
433
+ if [ -n "$seven_day_int" ] 2>/dev/null; then
434
+ _existing_mode="auto"
435
+ _existing_force=""
436
+ if [ -f "$_advisor_file" ]; then
437
+ _existing_mode=$(jq -r '.mode // "auto"' "$_advisor_file" 2>/dev/null)
438
+ _existing_force=$(jq -r '.forceModel // empty' "$_advisor_file" 2>/dev/null)
439
+ fi
440
+ if [ -n "$_existing_force" ]; then
441
+ printf '{"weeklyBudgetPercent":%d,"mode":"%s","forceModel":"%s"}\n' "$seven_day_int" "$_existing_mode" "$_existing_force" > "$_advisor_file" 2>/dev/null
442
+ else
443
+ printf '{"weeklyBudgetPercent":%d,"mode":"%s"}\n' "$seven_day_int" "$_existing_mode" > "$_advisor_file" 2>/dev/null
444
+ fi
445
+ fi
446
+
447
+ # Compute advisor mode label (mirrors thresholds in whisper-rules.ts)
448
+ # If mode is manually set (not "auto"), show that instead of auto-calculated
449
+ advisor_label=""
450
+ advisor_label_color=""
451
+ _display_mode="$_existing_mode"
452
+ if [ "$_display_mode" = "auto" ]; then
453
+ if [ "$seven_day_int" -ge 92 ] 2>/dev/null; then
454
+ _display_mode="critical"
455
+ elif [ "$seven_day_int" -ge 80 ] 2>/dev/null; then
456
+ _display_mode="strict"
457
+ elif [ "$seven_day_int" -ge 60 ] 2>/dev/null; then
458
+ _display_mode="conservative"
459
+ fi
460
+ fi
461
+ case "$_display_mode" in
462
+ "critical") advisor_label="critical"; advisor_label_color="$BRIGHT_RED" ;;
463
+ "strict") advisor_label="strict"; advisor_label_color="$BRIGHT_ORANGE" ;;
464
+ "conservative") advisor_label="conserve"; advisor_label_color="$BRIGHT_YELLOW" ;;
465
+ "normal") advisor_label="normal"; advisor_label_color="$BRIGHT_GREEN" ;;
466
+ esac
467
+ # Mark forced modes with a pin symbol so user knows it's not auto
468
+ if [ "$_existing_mode" != "auto" ] && [ -n "$advisor_label" ]; then
469
+ advisor_label="📌${advisor_label}"
470
+ fi
471
+
472
+ # Build usage suffix: 5h: 8% → 00:59 │ 1d: ● 29% / 36% │ 7d: ⚡strict 91% → Fr. 08:00
431
473
  five_label="5h: ${five_hour_int}%%"
432
474
  [ -n "$five_reset_fmt" ] && five_label="${five_label} → ${five_reset_fmt}"
433
- seven_label="7d: ${seven_day_int}%%"
475
+ seven_label="7d: "
476
+ [ -n "$advisor_label" ] && seven_label="${seven_label}${advisor_label_color}${advisor_label}${RESET} "
477
+ seven_label="${seven_label}${seven_day_int}%%"
434
478
  [ -n "$seven_reset_fmt" ] && seven_label="${seven_label} → ${seven_reset_fmt}"
435
479
 
436
480
  usage_suffix=" ${SEPARATOR_COLOR}│${RESET} ${five_color}${five_label}${RESET}"