aiblueprint-cli 1.4.59 → 1.4.61

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 (184) hide show
  1. package/README.md +16 -36
  2. package/agents-config/agents/action.md +1 -1
  3. package/agents-config/agents/explore-codebase.md +53 -53
  4. package/agents-config/agents/explore-docs.md +50 -69
  5. package/agents-config/agents/websearch.md +36 -40
  6. package/agents-config/claude-config/scripts/.claude/skills/fix-on-my-computer/SKILL.md +81 -0
  7. package/agents-config/claude-config/scripts/CLAUDE.md +10 -4
  8. package/agents-config/claude-config/scripts/bun.lockb +0 -0
  9. package/agents-config/claude-config/scripts/package.json +22 -30
  10. package/agents-config/claude-config/scripts/statusline/CLAUDE.md +37 -155
  11. package/agents-config/claude-config/scripts/statusline/README.md +18 -94
  12. package/agents-config/claude-config/scripts/statusline/defaults.json +13 -10
  13. package/agents-config/claude-config/scripts/statusline/fixtures/mock-transcript.jsonl +4 -4
  14. package/agents-config/claude-config/scripts/statusline/fixtures/test-input.json +4 -4
  15. package/agents-config/claude-config/scripts/statusline/src/commands/interactive-config.ts +403 -0
  16. package/agents-config/claude-config/scripts/statusline/src/index.ts +33 -82
  17. package/agents-config/claude-config/scripts/statusline/src/lib/config-types.ts +7 -1
  18. package/agents-config/claude-config/scripts/statusline/src/lib/formatters.ts +40 -0
  19. package/agents-config/claude-config/scripts/statusline/src/lib/presets.ts +13 -13
  20. package/agents-config/claude-config/scripts/statusline/src/lib/render-pure.ts +24 -5
  21. package/agents-config/claude-config/scripts/statusline/statusline.config.free.json +79 -0
  22. package/agents-config/claude-config/scripts/statusline/statusline.config.json +77 -77
  23. package/agents-config/codex-config/config.toml +9 -0
  24. package/agents-config/codex-config/hooks/command-deny-list.ts +203 -0
  25. package/agents-config/commands/prompts/create-vitejs-app.md +272 -0
  26. package/agents-config/commands/prompts/nextjs-add-prisma-db.md +136 -0
  27. package/agents-config/commands/prompts/nextjs-setup-better-auth.md +173 -0
  28. package/agents-config/commands/prompts/nextjs-setup-project.md +200 -0
  29. package/agents-config/commands/prompts/prompt.md +55 -0
  30. package/agents-config/commands/prompts/saas-challenge-idea.md +135 -0
  31. package/agents-config/commands/prompts/saas-create-architecture.md +242 -0
  32. package/agents-config/commands/prompts/saas-create-headline.md +132 -0
  33. package/agents-config/commands/prompts/saas-create-landing-copywritting.md +267 -0
  34. package/agents-config/commands/prompts/saas-create-legals-docs.md +176 -0
  35. package/agents-config/commands/prompts/saas-create-logos.md +240 -0
  36. package/agents-config/commands/prompts/saas-create-prd.md +195 -0
  37. package/agents-config/commands/prompts/saas-create-tasks.md +240 -0
  38. package/agents-config/commands/prompts/saas-define-pricing.md +293 -0
  39. package/agents-config/commands/prompts/saas-find-domain-name.md +190 -0
  40. package/agents-config/commands/prompts/saas-implement-landing-page.md +257 -0
  41. package/agents-config/commands/prompts/setup-tmux.md +160 -0
  42. package/agents-config/commands/prompts/tools.md +148 -0
  43. package/agents-config/scripts/.claude/skills/fix-on-my-computer/SKILL.md +81 -0
  44. package/agents-config/scripts/CLAUDE.md +37 -0
  45. package/agents-config/scripts/biome.json +37 -0
  46. package/agents-config/scripts/bun.lockb +0 -0
  47. package/agents-config/scripts/package.json +24 -0
  48. package/agents-config/scripts/statusline/CLAUDE.md +87 -0
  49. package/agents-config/scripts/statusline/README.md +117 -0
  50. package/agents-config/scripts/statusline/__tests__/context.test.ts +229 -0
  51. package/agents-config/scripts/statusline/__tests__/formatters.test.ts +108 -0
  52. package/agents-config/scripts/statusline/__tests__/statusline.test.ts +309 -0
  53. package/agents-config/scripts/statusline/defaults.json +82 -0
  54. package/agents-config/scripts/statusline/fixtures/mock-transcript.jsonl +4 -0
  55. package/agents-config/scripts/statusline/fixtures/test-input.json +35 -0
  56. package/agents-config/scripts/statusline/src/commands/interactive-config.ts +403 -0
  57. package/agents-config/scripts/statusline/src/index.ts +141 -0
  58. package/agents-config/scripts/statusline/src/lib/config-types.ts +110 -0
  59. package/agents-config/scripts/statusline/src/lib/config.ts +21 -0
  60. package/agents-config/scripts/statusline/src/lib/context.ts +103 -0
  61. package/agents-config/scripts/statusline/src/lib/formatters.ts +426 -0
  62. package/agents-config/scripts/statusline/src/lib/git.ts +100 -0
  63. package/agents-config/scripts/statusline/src/lib/menu-factories.ts +224 -0
  64. package/agents-config/scripts/statusline/src/lib/presets.ts +177 -0
  65. package/agents-config/scripts/statusline/src/lib/render-pure.ts +516 -0
  66. package/agents-config/scripts/statusline/src/lib/types.ts +36 -0
  67. package/agents-config/scripts/statusline/src/lib/utils.ts +15 -0
  68. package/agents-config/scripts/statusline/statusline.config.free.json +79 -0
  69. package/agents-config/scripts/statusline/statusline.config.json +79 -0
  70. package/agents-config/scripts/statusline/test-with-fixtures.ts +37 -0
  71. package/agents-config/scripts/statusline/test.ts +20 -0
  72. package/agents-config/scripts/statusline/tsconfig.json +27 -0
  73. package/agents-config/scripts/tsconfig.json +27 -0
  74. package/agents-config/skills/{subagent-creator → agents-managers}/SKILL.md +47 -47
  75. package/agents-config/skills/{subagent-creator/references/subagents.md → agents-managers/references/agents.md} +45 -45
  76. package/agents-config/skills/{subagent-creator → agents-managers}/references/context-management.md +20 -20
  77. package/agents-config/skills/{subagent-creator → agents-managers}/references/debugging-agents.md +27 -27
  78. package/agents-config/skills/{subagent-creator → agents-managers}/references/error-handling-and-recovery.md +19 -19
  79. package/agents-config/skills/{subagent-creator → agents-managers}/references/evaluation-and-testing.md +29 -29
  80. package/agents-config/skills/{subagent-creator → agents-managers}/references/orchestration-patterns.md +5 -5
  81. package/agents-config/skills/{subagent-creator/references/writing-subagent-prompts.md → agents-managers/references/writing-agent-prompts.md} +23 -23
  82. package/agents-config/skills/codex-environment/SKILL.md +2 -0
  83. package/agents-config/skills/commit/SKILL.md +2 -0
  84. package/agents-config/skills/create-pr/SKILL.md +2 -0
  85. package/agents-config/skills/environments-manager/SKILL.md +271 -0
  86. package/agents-config/skills/environments-manager/examples/claude/.worktreeinclude +3 -0
  87. package/agents-config/skills/environments-manager/examples/claude/commands/dev.md +5 -0
  88. package/agents-config/skills/environments-manager/examples/claude/commands/lint.md +5 -0
  89. package/agents-config/skills/environments-manager/examples/claude/commands/test.md +5 -0
  90. package/agents-config/skills/environments-manager/examples/claude/commands/typecheck.md +5 -0
  91. package/agents-config/skills/environments-manager/examples/claude/settings.json +24 -0
  92. package/agents-config/skills/environments-manager/examples/codex/environments/environment.toml +29 -0
  93. package/agents-config/skills/environments-manager/examples/cursor/worktrees.json +3 -0
  94. package/agents-config/skills/environments-manager/examples/scripts/claude-worktree-create.sh +96 -0
  95. package/agents-config/skills/environments-manager/examples/scripts/claude-worktree-remove.sh +66 -0
  96. package/agents-config/skills/environments-manager/examples/scripts/dev.sh +15 -0
  97. package/agents-config/skills/environments-manager/examples/scripts/worktree-down.sh +22 -0
  98. package/agents-config/skills/environments-manager/examples/scripts/worktree-up.sh +50 -0
  99. package/agents-config/skills/environments-manager/references/claude.md +156 -0
  100. package/agents-config/skills/environments-manager/references/codex.md +97 -0
  101. package/agents-config/skills/environments-manager/references/cursor.md +88 -0
  102. package/agents-config/skills/fix-pr-comments/SKILL.md +2 -0
  103. package/agents-config/skills/grill-me/SKILL.md +10 -0
  104. package/agents-config/skills/merge/SKILL.md +2 -0
  105. package/agents-config/skills/rules-manager/SKILL.md +191 -0
  106. package/agents-config/skills/rules-manager/references/agents-vs-claude.md +66 -0
  107. package/agents-config/skills/rules-manager/references/examples.md +117 -0
  108. package/agents-config/skills/skill-manager/SKILL.md +101 -0
  109. package/agents-config/skills/skill-manager/references/claude-code.md +81 -0
  110. package/agents-config/skills/skill-manager/references/codex.md +288 -0
  111. package/agents-config/skills/skill-manager/references/cursor.md +125 -0
  112. package/agents-config/skills/skill-manager/references/description-recommandation.md +97 -0
  113. package/agents-config/skills/skill-manager/scripts/inspect-description.ts +743 -0
  114. package/agents-config/skills/ultrathink/SKILL.md +2 -0
  115. package/dist/cli.js +581 -299
  116. package/package.json +1 -1
  117. package/agents-config/claude-config/scripts/statusline/data/.gitignore +0 -8
  118. package/agents-config/claude-config/scripts/statusline/data/.gitkeep +0 -0
  119. package/agents-config/claude-config/scripts/statusline/docs/ARCHITECTURE.md +0 -166
  120. package/agents-config/claude-config/scripts/statusline/src/tests/spend-v2.test.ts +0 -306
  121. package/agents-config/skills/apex/SKILL.md +0 -261
  122. package/agents-config/skills/apex/scripts/setup-templates.sh +0 -100
  123. package/agents-config/skills/apex/scripts/update-progress.sh +0 -80
  124. package/agents-config/skills/apex/steps/step-00-init.md +0 -267
  125. package/agents-config/skills/apex/steps/step-00b-branch.md +0 -126
  126. package/agents-config/skills/apex/steps/step-00b-economy.md +0 -244
  127. package/agents-config/skills/apex/steps/step-00b-interactive.md +0 -153
  128. package/agents-config/skills/apex/steps/step-01-analyze.md +0 -361
  129. package/agents-config/skills/apex/steps/step-02-plan.md +0 -264
  130. package/agents-config/skills/apex/steps/step-03-execute.md +0 -239
  131. package/agents-config/skills/apex/steps/step-04-validate.md +0 -251
  132. package/agents-config/skills/apex/templates/00-context.md +0 -43
  133. package/agents-config/skills/apex/templates/01-analyze.md +0 -10
  134. package/agents-config/skills/apex/templates/02-plan.md +0 -10
  135. package/agents-config/skills/apex/templates/03-execute.md +0 -10
  136. package/agents-config/skills/apex/templates/04-validate.md +0 -10
  137. package/agents-config/skills/apex/templates/README.md +0 -176
  138. package/agents-config/skills/apex/templates/step-complete.md +0 -7
  139. package/agents-config/skills/claude-memory/SKILL.md +0 -293
  140. package/agents-config/skills/claude-memory/references/comprehensive-example.md +0 -175
  141. package/agents-config/skills/claude-memory/references/optimize-guide.md +0 -300
  142. package/agents-config/skills/claude-memory/references/project-patterns.md +0 -334
  143. package/agents-config/skills/claude-memory/references/prompting-techniques.md +0 -411
  144. package/agents-config/skills/claude-memory/references/rules-directory-guide.md +0 -298
  145. package/agents-config/skills/claude-memory/references/section-templates.md +0 -347
  146. package/agents-config/skills/fix-errors/SKILL.md +0 -61
  147. package/agents-config/skills/fix-grammar/SKILL.md +0 -59
  148. package/agents-config/skills/ralph-loop/SKILL.md +0 -117
  149. package/agents-config/skills/ralph-loop/scripts/setup.sh +0 -278
  150. package/agents-config/skills/ralph-loop/steps/step-00-init.md +0 -215
  151. package/agents-config/skills/ralph-loop/steps/step-01-interactive-prd.md +0 -366
  152. package/agents-config/skills/ralph-loop/steps/step-02-create-stories.md +0 -273
  153. package/agents-config/skills/ralph-loop/steps/step-03-finish.md +0 -245
  154. package/agents-config/skills/skill-creator/LICENSE.txt +0 -202
  155. package/agents-config/skills/skill-creator/SKILL.md +0 -421
  156. package/agents-config/skills/skill-creator/package.json +0 -5
  157. package/agents-config/skills/skill-creator/references/output-patterns.md +0 -82
  158. package/agents-config/skills/skill-creator/references/progressive-disclosure-patterns.md +0 -374
  159. package/agents-config/skills/skill-creator/references/prompting-integration.md +0 -363
  160. package/agents-config/skills/skill-creator/references/real-world-examples.md +0 -513
  161. package/agents-config/skills/skill-creator/references/script-patterns.md +0 -385
  162. package/agents-config/skills/skill-creator/references/workflows.md +0 -28
  163. package/agents-config/skills/skill-creator/references/xml-tag-guide.md +0 -606
  164. package/agents-config/skills/skill-creator/scripts/init-skill.ts +0 -214
  165. package/agents-config/skills/skill-creator/scripts/package-skill.ts +0 -146
  166. package/agents-config/skills/skill-creator/scripts/validate.ts +0 -138
  167. package/agents-config/skills/workflow-apex-free/SKILL.md +0 -261
  168. package/agents-config/skills/workflow-apex-free/scripts/setup-templates.sh +0 -100
  169. package/agents-config/skills/workflow-apex-free/scripts/update-progress.sh +0 -80
  170. package/agents-config/skills/workflow-apex-free/steps/step-00-init.md +0 -267
  171. package/agents-config/skills/workflow-apex-free/steps/step-00b-branch.md +0 -126
  172. package/agents-config/skills/workflow-apex-free/steps/step-00b-economy.md +0 -244
  173. package/agents-config/skills/workflow-apex-free/steps/step-00b-interactive.md +0 -153
  174. package/agents-config/skills/workflow-apex-free/steps/step-01-analyze.md +0 -361
  175. package/agents-config/skills/workflow-apex-free/steps/step-02-plan.md +0 -264
  176. package/agents-config/skills/workflow-apex-free/steps/step-03-execute.md +0 -239
  177. package/agents-config/skills/workflow-apex-free/steps/step-04-validate.md +0 -251
  178. package/agents-config/skills/workflow-apex-free/templates/00-context.md +0 -43
  179. package/agents-config/skills/workflow-apex-free/templates/01-analyze.md +0 -10
  180. package/agents-config/skills/workflow-apex-free/templates/02-plan.md +0 -10
  181. package/agents-config/skills/workflow-apex-free/templates/03-execute.md +0 -10
  182. package/agents-config/skills/workflow-apex-free/templates/04-validate.md +0 -10
  183. package/agents-config/skills/workflow-apex-free/templates/README.md +0 -176
  184. package/agents-config/skills/workflow-apex-free/templates/step-complete.md +0 -7
@@ -0,0 +1,103 @@
1
+ import { existsSync } from "node:fs";
2
+
3
+ export interface TokenUsage {
4
+ input_tokens: number;
5
+ output_tokens: number;
6
+ cache_creation_input_tokens?: number;
7
+ cache_read_input_tokens?: number;
8
+ }
9
+
10
+ export interface TranscriptLine {
11
+ message?: { usage?: TokenUsage };
12
+ timestamp?: string;
13
+ isSidechain?: boolean;
14
+ isApiErrorMessage?: boolean;
15
+ }
16
+
17
+ export interface ContextResult {
18
+ tokens: number;
19
+ percentage: number;
20
+ }
21
+
22
+ export async function getContextLength(
23
+ transcriptPath: string,
24
+ ): Promise<number> {
25
+ try {
26
+ const content = await Bun.file(transcriptPath).text();
27
+ const lines = content.trim().split("\n");
28
+
29
+ if (lines.length === 0) return 0;
30
+
31
+ let mostRecentMainChainEntry: TranscriptLine | null = null;
32
+ let mostRecentTimestamp: Date | null = null;
33
+
34
+ for (const line of lines) {
35
+ try {
36
+ const data = JSON.parse(line) as TranscriptLine;
37
+
38
+ if (!data.message?.usage) continue;
39
+ if (data.isSidechain === true) continue;
40
+ if (data.isApiErrorMessage === true) continue;
41
+ if (!data.timestamp) continue;
42
+
43
+ const entryTime = new Date(data.timestamp);
44
+
45
+ if (!mostRecentTimestamp || entryTime > mostRecentTimestamp) {
46
+ mostRecentTimestamp = entryTime;
47
+ mostRecentMainChainEntry = data;
48
+ }
49
+ } catch {}
50
+ }
51
+
52
+ if (!mostRecentMainChainEntry?.message?.usage) {
53
+ return 0;
54
+ }
55
+
56
+ const usage = mostRecentMainChainEntry.message.usage;
57
+
58
+ return (
59
+ (usage.input_tokens || 0) +
60
+ (usage.cache_read_input_tokens ?? 0) +
61
+ (usage.cache_creation_input_tokens ?? 0)
62
+ );
63
+ } catch {
64
+ return 0;
65
+ }
66
+ }
67
+
68
+ export interface ContextDataParams {
69
+ transcriptPath: string;
70
+ maxContextTokens: number;
71
+ autocompactBufferTokens: number;
72
+ useUsableContextOnly?: boolean;
73
+ overheadTokens?: number;
74
+ }
75
+
76
+ export async function getContextData({
77
+ transcriptPath,
78
+ maxContextTokens,
79
+ autocompactBufferTokens,
80
+ useUsableContextOnly = false,
81
+ overheadTokens = 0,
82
+ }: ContextDataParams): Promise<ContextResult> {
83
+ if (!transcriptPath || !existsSync(transcriptPath)) {
84
+ return { tokens: 0, percentage: 0 };
85
+ }
86
+
87
+ const contextLength = await getContextLength(transcriptPath);
88
+ let totalTokens = contextLength + overheadTokens;
89
+
90
+ // If useUsableContextOnly is true, add the autocompact buffer to displayed tokens
91
+ if (useUsableContextOnly) {
92
+ totalTokens += autocompactBufferTokens;
93
+ }
94
+
95
+ // Always calculate percentage based on max context window
96
+ // (matching /context display behavior)
97
+ const percentage = Math.min(100, (totalTokens / maxContextTokens) * 100);
98
+
99
+ return {
100
+ tokens: totalTokens,
101
+ percentage: Math.round(percentage),
102
+ };
103
+ }
@@ -0,0 +1,426 @@
1
+ import { homedir } from "node:os";
2
+ import { sep } from "node:path";
3
+ import pc from "picocolors";
4
+ import type {
5
+ CostFormat,
6
+ ProgressBarBackground,
7
+ ProgressBarColor,
8
+ ProgressBarStyle,
9
+ StatuslineConfig,
10
+ } from "./config-types";
11
+ import type { GitStatus } from "./git";
12
+
13
+ type ColorFunction = (text: string | number) => string;
14
+
15
+ const pico = pc.createColors(true);
16
+
17
+ export const colors = {
18
+ green: pico.green as ColorFunction,
19
+ red: pico.red as ColorFunction,
20
+ purple: pico.magenta as ColorFunction,
21
+ yellow: pico.yellow as ColorFunction,
22
+ orange: ((text: string | number) =>
23
+ `\x1b[38;5;208m${text}\x1b[0m`) as ColorFunction,
24
+ peach: ((text: string | number) =>
25
+ `\x1b[38;2;222;115;86m${text}\x1b[0m`) as ColorFunction,
26
+ bgPeach: ((text: string | number) =>
27
+ `\x1b[48;2;222;115;86m${text}\x1b[0m`) as ColorFunction,
28
+ black: ((text: string | number) =>
29
+ `\x1b[38;2;0;0;0m${text}\x1b[39m`) as ColorFunction,
30
+ white: ((text: string | number) =>
31
+ `\x1b[38;2;255;255;255m${text}\x1b[39m`) as ColorFunction,
32
+ gray: pico.gray as ColorFunction,
33
+ dimWhite: ((text: string | number) =>
34
+ `\x1b[37m${text}\x1b[39m`) as ColorFunction,
35
+ lightGray: pico.whiteBright as ColorFunction,
36
+ cyan: pico.cyan as ColorFunction,
37
+ blue: pico.blue as ColorFunction,
38
+ bgBlack: pico.bgBlack as ColorFunction,
39
+ bgBlackBright: pico.bgBlackBright as ColorFunction,
40
+ bgWhite: pico.bgWhite as ColorFunction,
41
+ bgBlue: pico.bgBlue as ColorFunction,
42
+ bgMagenta: pico.bgMagenta as ColorFunction,
43
+ bgCyan: pico.bgCyan as ColorFunction,
44
+ dim: pico.dim as ColorFunction,
45
+ bold: pico.bold as ColorFunction,
46
+ hidden: pico.hidden as ColorFunction,
47
+ italic: pico.italic as ColorFunction,
48
+ underline: pico.underline as ColorFunction,
49
+ strikethrough: pico.strikethrough as ColorFunction,
50
+ reset: pico.reset as ColorFunction,
51
+ inverse: pico.inverse as ColorFunction,
52
+ } as const;
53
+
54
+ export function formatBranch(
55
+ git: GitStatus,
56
+ gitConfig: StatuslineConfig["git"],
57
+ ): string {
58
+ let result = "";
59
+
60
+ if (gitConfig.showBranch) {
61
+ result = colors.lightGray(git.branch);
62
+ }
63
+
64
+ if (git.hasChanges) {
65
+ const changes: string[] = [];
66
+
67
+ if (gitConfig.showDirtyIndicator) {
68
+ result += colors.purple("*");
69
+ }
70
+
71
+ if (gitConfig.showChanges) {
72
+ const totalAdded = git.staged.added + git.unstaged.added;
73
+ const totalDeleted = git.staged.deleted + git.unstaged.deleted;
74
+
75
+ if (totalAdded > 0) {
76
+ changes.push(colors.green(`+${totalAdded}`));
77
+ }
78
+ if (totalDeleted > 0) {
79
+ changes.push(colors.red(`-${totalDeleted}`));
80
+ }
81
+ }
82
+
83
+ if (gitConfig.showStaged && git.staged.files > 0) {
84
+ changes.push(colors.gray(`~${git.staged.files}`));
85
+ }
86
+
87
+ if (gitConfig.showUnstaged && git.unstaged.files > 0) {
88
+ changes.push(colors.yellow(`~${git.unstaged.files}`));
89
+ }
90
+
91
+ if (changes.length > 0) {
92
+ result += ` ${changes.join(" ")}`;
93
+ }
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ export function formatPath(
100
+ path: string,
101
+ mode: "full" | "truncated" | "basename" = "truncated",
102
+ ): string {
103
+ const home = homedir();
104
+ let formattedPath = path;
105
+
106
+ if (home && path.startsWith(home)) {
107
+ formattedPath = `~${path.slice(home.length)}`;
108
+ }
109
+
110
+ if (mode === "basename") {
111
+ const segments = path.split(/[/\\]/).filter((s) => s.length > 0);
112
+ return segments[segments.length - 1] || path;
113
+ }
114
+
115
+ if (mode === "truncated") {
116
+ const segments = formattedPath.split(/[/\\]/).filter((s) => s.length > 0);
117
+ if (segments.length > 2) {
118
+ return `…${sep}${segments.slice(-2).join(sep)}`;
119
+ }
120
+ }
121
+
122
+ return formattedPath;
123
+ }
124
+
125
+ export function formatCost(
126
+ cost: number,
127
+ format: CostFormat = "decimal1",
128
+ ): string {
129
+ if (format === "integer") return Math.round(cost).toString();
130
+ if (format === "decimal1") return cost.toFixed(1);
131
+ return cost.toFixed(2);
132
+ }
133
+
134
+ export function formatTokens(tokens: number, showDecimals = true): string {
135
+ if (tokens >= 1000000) {
136
+ const value = tokens / 1000000;
137
+ const number = showDecimals
138
+ ? value.toFixed(1)
139
+ : Math.round(value).toString();
140
+ return `${colors.lightGray(number)}${colors.gray("m")}`;
141
+ }
142
+ if (tokens >= 1000) {
143
+ const value = tokens / 1000;
144
+ const number = showDecimals
145
+ ? value.toFixed(1)
146
+ : Math.round(value).toString();
147
+ return `${colors.lightGray(number)}${colors.gray("k")}`;
148
+ }
149
+ return colors.lightGray(tokens.toString());
150
+ }
151
+
152
+ export function formatDuration(ms: number): string {
153
+ const minutes = Math.floor(ms / 60000);
154
+ const hours = Math.floor(minutes / 60);
155
+ const mins = minutes % 60;
156
+
157
+ if (hours > 0) {
158
+ return `${hours}h ${mins}m`;
159
+ }
160
+ return `${mins}m`;
161
+ }
162
+
163
+ export function formatResetTime(resetsAt: string): string {
164
+ try {
165
+ const resetDate = new Date(resetsAt);
166
+ if (Number.isNaN(resetDate.getTime())) {
167
+ return "N/A";
168
+ }
169
+
170
+ const now = new Date();
171
+ const diffMs = resetDate.getTime() - now.getTime();
172
+
173
+ if (diffMs <= 0) {
174
+ return "now";
175
+ }
176
+
177
+ const hours = Math.floor(diffMs / 3600000);
178
+ const minutes = Math.floor((diffMs % 3600000) / 60000);
179
+
180
+ if (hours > 0) {
181
+ return `${hours}h${minutes}m`;
182
+ }
183
+ return `${minutes}m`;
184
+ } catch {
185
+ return "N/A";
186
+ }
187
+ }
188
+
189
+ function getProgressBarColor(
190
+ percentage: number,
191
+ colorMode: ProgressBarColor,
192
+ ): ColorFunction {
193
+ if (colorMode === "progressive") {
194
+ if (percentage < 50) return colors.gray;
195
+ if (percentage < 70) return colors.yellow;
196
+ if (percentage < 90) return colors.orange;
197
+ return colors.red;
198
+ }
199
+ if (colorMode === "green") return colors.green;
200
+ if (colorMode === "yellow") return colors.yellow;
201
+ if (colorMode === "peach") return colors.peach;
202
+ if (colorMode === "black") return colors.black;
203
+ if (colorMode === "white") return colors.white;
204
+ if (colorMode === "purple") return colors.purple;
205
+ if (colorMode === "blue") return colors.blue;
206
+ if (colorMode === "cyan") return colors.cyan;
207
+ return colors.red;
208
+ }
209
+
210
+ function getProgressBarBackground(
211
+ background: ProgressBarBackground,
212
+ ): ColorFunction | null {
213
+ if (background === "none") return null;
214
+ if (background === "dark") return colors.bgBlack;
215
+ if (background === "gray") return colors.bgBlackBright;
216
+ if (background === "light") return colors.bgWhite;
217
+ if (background === "blue") return colors.bgBlue;
218
+ if (background === "purple") return colors.bgMagenta;
219
+ if (background === "cyan") return colors.bgCyan;
220
+ if (background === "peach") return colors.bgPeach;
221
+ return null;
222
+ }
223
+
224
+ export function formatProgressBarFilled(
225
+ percentage: number,
226
+ length: number,
227
+ colorMode: ProgressBarColor,
228
+ background: ProgressBarBackground,
229
+ ): string {
230
+ const filled = Math.round((percentage / 100) * length);
231
+ const empty = length - filled;
232
+
233
+ const filledBar = "█".repeat(filled);
234
+ const emptyBar = "░".repeat(empty);
235
+ const colorFn = getProgressBarColor(percentage, colorMode);
236
+ const bgFn = getProgressBarBackground(background);
237
+
238
+ const coloredFilled = bgFn ? bgFn(colorFn(filledBar)) : colorFn(filledBar);
239
+ const coloredEmpty = bgFn ? bgFn(colorFn(emptyBar)) : colorFn(emptyBar);
240
+
241
+ return `${coloredFilled}${coloredEmpty}`;
242
+ }
243
+
244
+ export function formatProgressBarRectangle(
245
+ percentage: number,
246
+ length: number,
247
+ colorMode: ProgressBarColor,
248
+ background: ProgressBarBackground,
249
+ ): string {
250
+ const filled = Math.round((percentage / 100) * length);
251
+ const empty = length - filled;
252
+
253
+ const filledBar = "▰".repeat(filled);
254
+ const emptyBar = "▱".repeat(empty);
255
+ const colorFn = getProgressBarColor(percentage, colorMode);
256
+ const bgFn = getProgressBarBackground(background);
257
+
258
+ const coloredFilled = bgFn ? bgFn(colorFn(filledBar)) : colorFn(filledBar);
259
+ const coloredEmpty = bgFn ? bgFn(colorFn(emptyBar)) : colorFn(emptyBar);
260
+
261
+ return `${coloredFilled}${coloredEmpty}`;
262
+ }
263
+
264
+ export function formatProgressBarBraille(
265
+ percentage: number,
266
+ length: number,
267
+ colorMode: ProgressBarColor,
268
+ background: ProgressBarBackground,
269
+ ): string {
270
+ const brailleChars = ["⣀", "⣄", "⣤", "⣦", "⣶", "⣷", "⣿"];
271
+
272
+ const totalSteps = length * (brailleChars.length - 1);
273
+ const currentStep = Math.round((percentage / 100) * totalSteps);
274
+
275
+ const fullBlocks = Math.floor(currentStep / (brailleChars.length - 1));
276
+ const partialIndex = currentStep % (brailleChars.length - 1);
277
+ const emptyBlocks = length - fullBlocks - (partialIndex > 0 ? 1 : 0);
278
+
279
+ const colorFn = getProgressBarColor(percentage, colorMode);
280
+ const bgFn = getProgressBarBackground(background);
281
+
282
+ const fullPart = bgFn
283
+ ? bgFn(colorFn("⣿".repeat(fullBlocks)))
284
+ : colorFn("⣿".repeat(fullBlocks));
285
+ const partialPart =
286
+ partialIndex > 0
287
+ ? bgFn
288
+ ? bgFn(colorFn(brailleChars[partialIndex]))
289
+ : colorFn(brailleChars[partialIndex])
290
+ : "";
291
+ const emptyPart =
292
+ emptyBlocks > 0
293
+ ? bgFn
294
+ ? bgFn(colorFn("⣀".repeat(emptyBlocks)))
295
+ : colorFn("⣀".repeat(emptyBlocks))
296
+ : "";
297
+
298
+ return `${fullPart}${partialPart}${emptyPart}`;
299
+ }
300
+
301
+ export function formatProgressBar({
302
+ percentage,
303
+ length,
304
+ style,
305
+ colorMode,
306
+ background,
307
+ }: {
308
+ percentage: number;
309
+ length: 5 | 10 | 15;
310
+ style: ProgressBarStyle;
311
+ colorMode: ProgressBarColor;
312
+ background: ProgressBarBackground;
313
+ }): string {
314
+ if (style === "rectangle") {
315
+ return formatProgressBarRectangle(
316
+ percentage,
317
+ length,
318
+ colorMode,
319
+ background,
320
+ );
321
+ }
322
+ if (style === "braille") {
323
+ return formatProgressBarBraille(percentage, length, colorMode, background);
324
+ }
325
+ return formatProgressBarFilled(percentage, length, colorMode, background);
326
+ }
327
+
328
+ export function formatDualBar({
329
+ leftLabel,
330
+ leftPercentage,
331
+ leftColorFn,
332
+ rightLabel,
333
+ rightValue,
334
+ rightColorFn,
335
+ barLength,
336
+ }: {
337
+ leftLabel: string;
338
+ leftPercentage: number;
339
+ leftColorFn: ColorFunction;
340
+ rightLabel: string;
341
+ rightValue: number;
342
+ rightColorFn: ColorFunction;
343
+ barLength: number;
344
+ }): string {
345
+ const leftFilled = Math.round((leftPercentage / 100) * barLength);
346
+ const leftEmpty = barLength - leftFilled;
347
+ const leftBar = leftColorFn("█".repeat(leftFilled) + "░".repeat(leftEmpty));
348
+
349
+ const absRightValue = Math.abs(rightValue);
350
+ const rightFilled = Math.round(
351
+ (Math.min(absRightValue, 50) / 50) * barLength,
352
+ );
353
+ const rightEmpty = barLength - rightFilled;
354
+ const rightBar = rightColorFn(
355
+ "█".repeat(rightFilled) + "░".repeat(rightEmpty),
356
+ );
357
+
358
+ const leftText = `${leftColorFn(leftLabel)} ${leftBar} ${colors.lightGray(leftPercentage.toFixed(1))}${colors.gray("%")}`;
359
+ const sign = rightValue >= 0 ? "+" : "";
360
+ const rightText = `${rightColorFn(rightLabel)} ${rightBar} ${rightColorFn(`${sign}${rightValue.toFixed(1)}%`)}`;
361
+
362
+ return `${leftText}\n${rightText}`;
363
+ }
364
+
365
+ export function formatSession(
366
+ cost: string,
367
+ duration: string,
368
+ tokensUsed: number,
369
+ tokensMax: number,
370
+ percentage: number,
371
+ config: StatuslineConfig["session"],
372
+ ): string {
373
+ const sessionItems: string[] = [];
374
+
375
+ if (config.cost.enabled) {
376
+ sessionItems.push(`${colors.gray("$")}${colors.dimWhite(cost)}`);
377
+ }
378
+
379
+ if (config.tokens.enabled) {
380
+ const formattedUsed = formatTokens(tokensUsed, config.tokens.showDecimals);
381
+ if (config.tokens.showMax) {
382
+ const formattedMax = formatTokens(tokensMax, config.tokens.showDecimals);
383
+ sessionItems.push(`${formattedUsed}${colors.gray("/")}${formattedMax}`);
384
+ } else {
385
+ sessionItems.push(formattedUsed);
386
+ }
387
+ }
388
+
389
+ if (config.percentage.enabled) {
390
+ const parts: string[] = [];
391
+
392
+ if (config.percentage.progressBar.enabled) {
393
+ const bar = formatProgressBar({
394
+ percentage,
395
+ length: config.percentage.progressBar.length,
396
+ style: config.percentage.progressBar.style,
397
+ colorMode: config.percentage.progressBar.color,
398
+ background: config.percentage.progressBar.background,
399
+ });
400
+ parts.push(bar);
401
+ }
402
+
403
+ if (config.percentage.showValue) {
404
+ parts.push(
405
+ `${colors.lightGray(percentage.toString())}${colors.gray("%")}`,
406
+ );
407
+ }
408
+
409
+ if (parts.length > 0) {
410
+ sessionItems.push(parts.join(" "));
411
+ }
412
+ }
413
+
414
+ if (config.duration.enabled) {
415
+ sessionItems.push(colors.gray(`(${duration})`));
416
+ }
417
+
418
+ if (sessionItems.length === 0) {
419
+ return "";
420
+ }
421
+
422
+ const infoSep = config.infoSeparator
423
+ ? ` ${colors.gray(config.infoSeparator)} `
424
+ : " ";
425
+ return `${colors.gray("S:")} ${sessionItems.join(infoSep)}`;
426
+ }
@@ -0,0 +1,100 @@
1
+ import { $ } from "bun";
2
+
3
+ export interface GitStatus {
4
+ branch: string;
5
+ hasChanges: boolean;
6
+ staged: {
7
+ added: number;
8
+ deleted: number;
9
+ files: number;
10
+ };
11
+ unstaged: {
12
+ added: number;
13
+ deleted: number;
14
+ files: number;
15
+ };
16
+ }
17
+
18
+ export async function getGitStatus(): Promise<GitStatus> {
19
+ try {
20
+ const isGitRepo = await $`git rev-parse --git-dir`.quiet().nothrow();
21
+ if (isGitRepo.exitCode !== 0) {
22
+ return {
23
+ branch: "no-git",
24
+ hasChanges: false,
25
+ staged: { added: 0, deleted: 0, files: 0 },
26
+ unstaged: { added: 0, deleted: 0, files: 0 },
27
+ };
28
+ }
29
+
30
+ const branchResult = await $`git branch --show-current`.quiet().text();
31
+ const branch = branchResult.trim() || "detached";
32
+
33
+ const diffCheck = await $`git diff-index --quiet HEAD --`.quiet().nothrow();
34
+ const cachedCheck = await $`git diff-index --quiet --cached HEAD --`
35
+ .quiet()
36
+ .nothrow();
37
+
38
+ if (diffCheck.exitCode !== 0 || cachedCheck.exitCode !== 0) {
39
+ const unstagedDiff = await $`git diff --numstat`.quiet().text();
40
+ const stagedDiff = await $`git diff --cached --numstat`.quiet().text();
41
+ const stagedFilesResult = await $`git diff --cached --name-only`
42
+ .quiet()
43
+ .text();
44
+ const unstagedFilesResult = await $`git diff --name-only`.quiet().text();
45
+
46
+ const parseStats = (diff: string) => {
47
+ let added = 0;
48
+ let deleted = 0;
49
+ for (const line of diff.split("\n")) {
50
+ if (!line.trim()) continue;
51
+ const [a, d] = line
52
+ .split("\t")
53
+ .map((n) => Number.parseInt(n, 10) || 0);
54
+ added += a;
55
+ deleted += d;
56
+ }
57
+ return { added, deleted };
58
+ };
59
+
60
+ const unstagedStats = parseStats(unstagedDiff);
61
+ const stagedStats = parseStats(stagedDiff);
62
+
63
+ const stagedFilesCount = stagedFilesResult
64
+ .split("\n")
65
+ .filter((f) => f.trim()).length;
66
+ const unstagedFilesCount = unstagedFilesResult
67
+ .split("\n")
68
+ .filter((f) => f.trim()).length;
69
+
70
+ return {
71
+ branch,
72
+ hasChanges: true,
73
+ staged: {
74
+ added: stagedStats.added,
75
+ deleted: stagedStats.deleted,
76
+ files: stagedFilesCount,
77
+ },
78
+ unstaged: {
79
+ added: unstagedStats.added,
80
+ deleted: unstagedStats.deleted,
81
+ files: unstagedFilesCount,
82
+ },
83
+ };
84
+ }
85
+
86
+ return {
87
+ branch,
88
+ hasChanges: false,
89
+ staged: { added: 0, deleted: 0, files: 0 },
90
+ unstaged: { added: 0, deleted: 0, files: 0 },
91
+ };
92
+ } catch {
93
+ return {
94
+ branch: "no-git",
95
+ hasChanges: false,
96
+ staged: { added: 0, deleted: 0, files: 0 },
97
+ unstaged: { added: 0, deleted: 0, files: 0 },
98
+ };
99
+ }
100
+ }