aiblueprint-cli 1.4.58 → 1.4.60

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 (180) hide show
  1. package/README.md +19 -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/commands/prompts/create-vitejs-app.md +272 -0
  24. package/agents-config/commands/prompts/nextjs-add-prisma-db.md +136 -0
  25. package/agents-config/commands/prompts/nextjs-setup-better-auth.md +173 -0
  26. package/agents-config/commands/prompts/nextjs-setup-project.md +200 -0
  27. package/agents-config/commands/prompts/prompt.md +55 -0
  28. package/agents-config/commands/prompts/saas-challenge-idea.md +135 -0
  29. package/agents-config/commands/prompts/saas-create-architecture.md +242 -0
  30. package/agents-config/commands/prompts/saas-create-headline.md +132 -0
  31. package/agents-config/commands/prompts/saas-create-landing-copywritting.md +267 -0
  32. package/agents-config/commands/prompts/saas-create-legals-docs.md +176 -0
  33. package/agents-config/commands/prompts/saas-create-logos.md +240 -0
  34. package/agents-config/commands/prompts/saas-create-prd.md +195 -0
  35. package/agents-config/commands/prompts/saas-create-tasks.md +240 -0
  36. package/agents-config/commands/prompts/saas-define-pricing.md +293 -0
  37. package/agents-config/commands/prompts/saas-find-domain-name.md +190 -0
  38. package/agents-config/commands/prompts/saas-implement-landing-page.md +257 -0
  39. package/agents-config/commands/prompts/setup-tmux.md +160 -0
  40. package/agents-config/commands/prompts/tools.md +148 -0
  41. package/agents-config/scripts/.claude/skills/fix-on-my-computer/SKILL.md +81 -0
  42. package/agents-config/scripts/CLAUDE.md +37 -0
  43. package/agents-config/scripts/biome.json +37 -0
  44. package/agents-config/scripts/bun.lockb +0 -0
  45. package/agents-config/scripts/package.json +24 -0
  46. package/agents-config/scripts/statusline/CLAUDE.md +87 -0
  47. package/agents-config/scripts/statusline/README.md +117 -0
  48. package/agents-config/scripts/statusline/__tests__/context.test.ts +229 -0
  49. package/agents-config/scripts/statusline/__tests__/formatters.test.ts +108 -0
  50. package/agents-config/scripts/statusline/__tests__/statusline.test.ts +309 -0
  51. package/agents-config/scripts/statusline/defaults.json +82 -0
  52. package/agents-config/scripts/statusline/fixtures/mock-transcript.jsonl +4 -0
  53. package/agents-config/scripts/statusline/fixtures/test-input.json +35 -0
  54. package/agents-config/scripts/statusline/src/commands/interactive-config.ts +403 -0
  55. package/agents-config/scripts/statusline/src/index.ts +141 -0
  56. package/agents-config/scripts/statusline/src/lib/config-types.ts +110 -0
  57. package/agents-config/scripts/statusline/src/lib/config.ts +21 -0
  58. package/agents-config/scripts/statusline/src/lib/context.ts +103 -0
  59. package/agents-config/scripts/statusline/src/lib/formatters.ts +426 -0
  60. package/agents-config/scripts/statusline/src/lib/git.ts +100 -0
  61. package/agents-config/scripts/statusline/src/lib/menu-factories.ts +224 -0
  62. package/agents-config/scripts/statusline/src/lib/presets.ts +177 -0
  63. package/agents-config/scripts/statusline/src/lib/render-pure.ts +516 -0
  64. package/agents-config/scripts/statusline/src/lib/types.ts +36 -0
  65. package/agents-config/scripts/statusline/src/lib/utils.ts +15 -0
  66. package/agents-config/scripts/statusline/statusline.config.free.json +79 -0
  67. package/agents-config/scripts/statusline/statusline.config.json +79 -0
  68. package/agents-config/scripts/statusline/test-with-fixtures.ts +37 -0
  69. package/agents-config/scripts/statusline/test.ts +20 -0
  70. package/agents-config/scripts/statusline/tsconfig.json +27 -0
  71. package/agents-config/scripts/tsconfig.json +27 -0
  72. package/agents-config/skills/{subagent-creator → agents-managers}/SKILL.md +47 -47
  73. package/agents-config/skills/{subagent-creator/references/subagents.md → agents-managers/references/agents.md} +45 -45
  74. package/agents-config/skills/{subagent-creator → agents-managers}/references/context-management.md +20 -20
  75. package/agents-config/skills/{subagent-creator → agents-managers}/references/debugging-agents.md +27 -27
  76. package/agents-config/skills/{subagent-creator → agents-managers}/references/error-handling-and-recovery.md +19 -19
  77. package/agents-config/skills/{subagent-creator → agents-managers}/references/evaluation-and-testing.md +29 -29
  78. package/agents-config/skills/{subagent-creator → agents-managers}/references/orchestration-patterns.md +5 -5
  79. package/agents-config/skills/{subagent-creator/references/writing-subagent-prompts.md → agents-managers/references/writing-agent-prompts.md} +23 -23
  80. package/agents-config/skills/codex-environment/SKILL.md +2 -0
  81. package/agents-config/skills/commit/SKILL.md +2 -0
  82. package/agents-config/skills/create-pr/SKILL.md +2 -0
  83. package/agents-config/skills/environments-manager/SKILL.md +271 -0
  84. package/agents-config/skills/environments-manager/examples/claude/.worktreeinclude +3 -0
  85. package/agents-config/skills/environments-manager/examples/claude/commands/dev.md +5 -0
  86. package/agents-config/skills/environments-manager/examples/claude/commands/lint.md +5 -0
  87. package/agents-config/skills/environments-manager/examples/claude/commands/test.md +5 -0
  88. package/agents-config/skills/environments-manager/examples/claude/commands/typecheck.md +5 -0
  89. package/agents-config/skills/environments-manager/examples/claude/settings.json +24 -0
  90. package/agents-config/skills/environments-manager/examples/codex/environments/environment.toml +29 -0
  91. package/agents-config/skills/environments-manager/examples/cursor/worktrees.json +3 -0
  92. package/agents-config/skills/environments-manager/examples/scripts/claude-worktree-create.sh +96 -0
  93. package/agents-config/skills/environments-manager/examples/scripts/claude-worktree-remove.sh +66 -0
  94. package/agents-config/skills/environments-manager/examples/scripts/dev.sh +15 -0
  95. package/agents-config/skills/environments-manager/examples/scripts/worktree-down.sh +22 -0
  96. package/agents-config/skills/environments-manager/examples/scripts/worktree-up.sh +50 -0
  97. package/agents-config/skills/environments-manager/references/claude.md +156 -0
  98. package/agents-config/skills/environments-manager/references/codex.md +97 -0
  99. package/agents-config/skills/environments-manager/references/cursor.md +88 -0
  100. package/agents-config/skills/fix-pr-comments/SKILL.md +2 -0
  101. package/agents-config/skills/grill-me/SKILL.md +10 -0
  102. package/agents-config/skills/merge/SKILL.md +2 -0
  103. package/agents-config/skills/rules-manager/SKILL.md +191 -0
  104. package/agents-config/skills/rules-manager/references/agents-vs-claude.md +66 -0
  105. package/agents-config/skills/rules-manager/references/examples.md +117 -0
  106. package/agents-config/skills/skill-manager/SKILL.md +83 -0
  107. package/agents-config/skills/skill-manager/references/claude-code.md +81 -0
  108. package/agents-config/skills/skill-manager/references/codex.md +288 -0
  109. package/agents-config/skills/skill-manager/references/cursor.md +125 -0
  110. package/agents-config/skills/ultrathink/SKILL.md +2 -0
  111. package/dist/cli.js +618 -182
  112. package/package.json +1 -1
  113. package/agents-config/claude-config/scripts/statusline/data/.gitignore +0 -8
  114. package/agents-config/claude-config/scripts/statusline/data/.gitkeep +0 -0
  115. package/agents-config/claude-config/scripts/statusline/docs/ARCHITECTURE.md +0 -166
  116. package/agents-config/claude-config/scripts/statusline/src/tests/spend-v2.test.ts +0 -306
  117. package/agents-config/skills/apex/SKILL.md +0 -261
  118. package/agents-config/skills/apex/scripts/setup-templates.sh +0 -100
  119. package/agents-config/skills/apex/scripts/update-progress.sh +0 -80
  120. package/agents-config/skills/apex/steps/step-00-init.md +0 -267
  121. package/agents-config/skills/apex/steps/step-00b-branch.md +0 -126
  122. package/agents-config/skills/apex/steps/step-00b-economy.md +0 -244
  123. package/agents-config/skills/apex/steps/step-00b-interactive.md +0 -153
  124. package/agents-config/skills/apex/steps/step-01-analyze.md +0 -361
  125. package/agents-config/skills/apex/steps/step-02-plan.md +0 -264
  126. package/agents-config/skills/apex/steps/step-03-execute.md +0 -239
  127. package/agents-config/skills/apex/steps/step-04-validate.md +0 -251
  128. package/agents-config/skills/apex/templates/00-context.md +0 -43
  129. package/agents-config/skills/apex/templates/01-analyze.md +0 -10
  130. package/agents-config/skills/apex/templates/02-plan.md +0 -10
  131. package/agents-config/skills/apex/templates/03-execute.md +0 -10
  132. package/agents-config/skills/apex/templates/04-validate.md +0 -10
  133. package/agents-config/skills/apex/templates/README.md +0 -176
  134. package/agents-config/skills/apex/templates/step-complete.md +0 -7
  135. package/agents-config/skills/claude-memory/SKILL.md +0 -293
  136. package/agents-config/skills/claude-memory/references/comprehensive-example.md +0 -175
  137. package/agents-config/skills/claude-memory/references/optimize-guide.md +0 -300
  138. package/agents-config/skills/claude-memory/references/project-patterns.md +0 -334
  139. package/agents-config/skills/claude-memory/references/prompting-techniques.md +0 -411
  140. package/agents-config/skills/claude-memory/references/rules-directory-guide.md +0 -298
  141. package/agents-config/skills/claude-memory/references/section-templates.md +0 -347
  142. package/agents-config/skills/fix-errors/SKILL.md +0 -61
  143. package/agents-config/skills/fix-grammar/SKILL.md +0 -59
  144. package/agents-config/skills/ralph-loop/SKILL.md +0 -117
  145. package/agents-config/skills/ralph-loop/scripts/setup.sh +0 -278
  146. package/agents-config/skills/ralph-loop/steps/step-00-init.md +0 -215
  147. package/agents-config/skills/ralph-loop/steps/step-01-interactive-prd.md +0 -366
  148. package/agents-config/skills/ralph-loop/steps/step-02-create-stories.md +0 -273
  149. package/agents-config/skills/ralph-loop/steps/step-03-finish.md +0 -245
  150. package/agents-config/skills/skill-creator/LICENSE.txt +0 -202
  151. package/agents-config/skills/skill-creator/SKILL.md +0 -421
  152. package/agents-config/skills/skill-creator/package.json +0 -5
  153. package/agents-config/skills/skill-creator/references/output-patterns.md +0 -82
  154. package/agents-config/skills/skill-creator/references/progressive-disclosure-patterns.md +0 -374
  155. package/agents-config/skills/skill-creator/references/prompting-integration.md +0 -363
  156. package/agents-config/skills/skill-creator/references/real-world-examples.md +0 -513
  157. package/agents-config/skills/skill-creator/references/script-patterns.md +0 -385
  158. package/agents-config/skills/skill-creator/references/workflows.md +0 -28
  159. package/agents-config/skills/skill-creator/references/xml-tag-guide.md +0 -606
  160. package/agents-config/skills/skill-creator/scripts/init-skill.ts +0 -214
  161. package/agents-config/skills/skill-creator/scripts/package-skill.ts +0 -146
  162. package/agents-config/skills/skill-creator/scripts/validate.ts +0 -138
  163. package/agents-config/skills/workflow-apex-free/SKILL.md +0 -261
  164. package/agents-config/skills/workflow-apex-free/scripts/setup-templates.sh +0 -100
  165. package/agents-config/skills/workflow-apex-free/scripts/update-progress.sh +0 -80
  166. package/agents-config/skills/workflow-apex-free/steps/step-00-init.md +0 -267
  167. package/agents-config/skills/workflow-apex-free/steps/step-00b-branch.md +0 -126
  168. package/agents-config/skills/workflow-apex-free/steps/step-00b-economy.md +0 -244
  169. package/agents-config/skills/workflow-apex-free/steps/step-00b-interactive.md +0 -153
  170. package/agents-config/skills/workflow-apex-free/steps/step-01-analyze.md +0 -361
  171. package/agents-config/skills/workflow-apex-free/steps/step-02-plan.md +0 -264
  172. package/agents-config/skills/workflow-apex-free/steps/step-03-execute.md +0 -239
  173. package/agents-config/skills/workflow-apex-free/steps/step-04-validate.md +0 -251
  174. package/agents-config/skills/workflow-apex-free/templates/00-context.md +0 -43
  175. package/agents-config/skills/workflow-apex-free/templates/01-analyze.md +0 -10
  176. package/agents-config/skills/workflow-apex-free/templates/02-plan.md +0 -10
  177. package/agents-config/skills/workflow-apex-free/templates/03-execute.md +0 -10
  178. package/agents-config/skills/workflow-apex-free/templates/04-validate.md +0 -10
  179. package/agents-config/skills/workflow-apex-free/templates/README.md +0 -176
  180. package/agents-config/skills/workflow-apex-free/templates/step-complete.md +0 -7
@@ -0,0 +1,403 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { readFileSync, writeFileSync } from "node:fs";
4
+ import { join } from "node:path";
5
+ import { defaultConfig, type StatuslineConfig } from "../lib/config";
6
+ import { colors } from "../lib/formatters";
7
+ import {
8
+ cycle,
9
+ type MenuOption,
10
+ PATH_DISPLAY_MODES,
11
+ PROGRESS_BAR_COLORS,
12
+ PROGRESS_BAR_LENGTHS,
13
+ SEPARATORS,
14
+ toggle,
15
+ } from "../lib/menu-factories";
16
+ import { PRESETS } from "../lib/presets";
17
+ import {
18
+ type RawStatuslineData,
19
+ renderStatuslineRaw,
20
+ } from "../lib/render-pure";
21
+
22
+ const CONFIG_FILE_PATH = join(
23
+ import.meta.dir,
24
+ "..",
25
+ "..",
26
+ "statusline.config.json",
27
+ );
28
+
29
+ // ─────────────────────────────────────────────────────────────
30
+ // RAW MOCK DATA
31
+ // ─────────────────────────────────────────────────────────────
32
+
33
+ const MOCK_DATA: RawStatuslineData = {
34
+ git: {
35
+ branch: "main",
36
+ dirty: true,
37
+ staged: { files: 2, added: 45, deleted: 12 },
38
+ unstaged: { files: 3, added: 23, deleted: 8 },
39
+ },
40
+ path: "/Users/dev/.claude/project",
41
+ modelName: "Sonnet 4.5",
42
+ cost: 0.24,
43
+ durationMs: 720000,
44
+ contextTokens: 75000,
45
+ contextPercentage: 38,
46
+ usageLimits: {
47
+ five_hour: {
48
+ utilization: 29,
49
+ resets_at: new Date(Date.now() + 3 * 60 * 60 * 1000).toISOString(),
50
+ },
51
+ seven_day: {
52
+ utilization: 45,
53
+ resets_at: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000).toISOString(),
54
+ },
55
+ },
56
+ periodCost: 36,
57
+ todayCost: 12.5,
58
+ };
59
+
60
+ // ─────────────────────────────────────────────────────────────
61
+ // HELPER: Progress bar style cycle (includes "None")
62
+ // ─────────────────────────────────────────────────────────────
63
+
64
+ const PROGRESS_STYLES = ["None", "Braille", "Rectangle", "Filled"] as const;
65
+
66
+ function progressStyleCycle(
67
+ basePath: string,
68
+ parentHidden: (c: StatuslineConfig) => boolean,
69
+ ): MenuOption {
70
+ return {
71
+ path: `${basePath}.progressBar.style`,
72
+ label: "Progress bar",
73
+ type: "cycle",
74
+ choices: [...PROGRESS_STYLES],
75
+ getValue: (c) => {
76
+ const bar = getNestedValue(c, `${basePath}.progressBar`) as {
77
+ enabled: boolean;
78
+ style: string;
79
+ };
80
+ if (!bar.enabled) return "None";
81
+ return bar.style.charAt(0).toUpperCase() + bar.style.slice(1);
82
+ },
83
+ cycle: (c, dir) => {
84
+ const bar = getNestedValue(c, `${basePath}.progressBar`) as {
85
+ enabled: boolean;
86
+ style: string;
87
+ };
88
+ const current = bar.enabled
89
+ ? bar.style.charAt(0).toUpperCase() + bar.style.slice(1)
90
+ : "None";
91
+ const idx = PROGRESS_STYLES.indexOf(
92
+ current as (typeof PROGRESS_STYLES)[number],
93
+ );
94
+ const next =
95
+ PROGRESS_STYLES[
96
+ (idx + dir + PROGRESS_STYLES.length) % PROGRESS_STYLES.length
97
+ ];
98
+ if (next === "None") {
99
+ setNestedValue(c, `${basePath}.progressBar.enabled`, false);
100
+ } else {
101
+ setNestedValue(c, `${basePath}.progressBar.enabled`, true);
102
+ setNestedValue(c, `${basePath}.progressBar.style`, next.toLowerCase());
103
+ }
104
+ },
105
+ hidden: parentHidden,
106
+ };
107
+ }
108
+
109
+ function getNestedValue(obj: unknown, path: string): unknown {
110
+ return path.split(".").reduce((current, key) => {
111
+ if (current && typeof current === "object" && key in current) {
112
+ return (current as Record<string, unknown>)[key];
113
+ }
114
+ return undefined;
115
+ }, obj);
116
+ }
117
+
118
+ function setNestedValue(obj: unknown, path: string, value: unknown): void {
119
+ const keys = path.split(".");
120
+ const lastKey = keys.pop();
121
+ if (!lastKey) return;
122
+ let current = obj;
123
+ for (const key of keys) {
124
+ if (current && typeof current === "object" && key in current) {
125
+ current = (current as Record<string, unknown>)[key];
126
+ } else {
127
+ return;
128
+ }
129
+ }
130
+ if (current && typeof current === "object") {
131
+ (current as Record<string, unknown>)[lastKey] = value;
132
+ }
133
+ }
134
+
135
+ // ─────────────────────────────────────────────────────────────
136
+ // MENU STRUCTURE
137
+ // ─────────────────────────────────────────────────────────────
138
+
139
+ interface Tab {
140
+ id: string;
141
+ label: string;
142
+ options: MenuOption[];
143
+ }
144
+
145
+ const tabs: Tab[] = [
146
+ {
147
+ id: "presets",
148
+ label: "PRESETS",
149
+ options: PRESETS.map((preset, index) => ({
150
+ path: `preset.${index}`,
151
+ label: preset.name,
152
+ description: preset.description,
153
+ type: "boolean" as const,
154
+ getValue: () => false,
155
+ toggle: () => {},
156
+ isPreset: true,
157
+ presetIndex: index,
158
+ })),
159
+ },
160
+ {
161
+ id: "session",
162
+ label: "SESSION",
163
+ options: [
164
+ toggle("session.duration.enabled", "Duration"),
165
+ toggle("session.tokens.enabled", "Token count"),
166
+ toggle("session.tokens.showMax", " Show max", {
167
+ hidden: (c) => !c.session.tokens.enabled,
168
+ }),
169
+ toggle("session.tokens.showDecimals", " Decimals", {
170
+ hidden: (c) => !c.session.tokens.enabled,
171
+ }),
172
+ toggle("session.percentage.enabled", "Context %"),
173
+ toggle("session.percentage.showValue", " Show value", {
174
+ hidden: (c) => !c.session.percentage.enabled,
175
+ }),
176
+ progressStyleCycle(
177
+ "session.percentage",
178
+ (c) => !c.session.percentage.enabled,
179
+ ),
180
+ cycle(
181
+ "session.percentage.progressBar.length",
182
+ " Length",
183
+ PROGRESS_BAR_LENGTHS,
184
+ {
185
+ hidden: (c) =>
186
+ !c.session.percentage.enabled ||
187
+ !c.session.percentage.progressBar.enabled,
188
+ },
189
+ ),
190
+ cycle(
191
+ "session.percentage.progressBar.color",
192
+ " Color",
193
+ PROGRESS_BAR_COLORS,
194
+ {
195
+ hidden: (c) =>
196
+ !c.session.percentage.enabled ||
197
+ !c.session.percentage.progressBar.enabled,
198
+ },
199
+ ),
200
+ toggle("context.useUsableContextOnly", "45k buffer"),
201
+ ],
202
+ },
203
+ {
204
+ id: "git",
205
+ label: "GIT",
206
+ options: [
207
+ {
208
+ path: "git.enabled",
209
+ label: "Git info",
210
+ type: "cycle" as const,
211
+ choices: ["Enabled", "Disabled"],
212
+ getValue: (c) => (c.git.enabled ? "Enabled" : "Disabled"),
213
+ cycle: (c) => {
214
+ c.git.enabled = !c.git.enabled;
215
+ },
216
+ },
217
+ toggle("git.showBranch", " Branch name", {
218
+ hidden: (c) => !c.git.enabled,
219
+ }),
220
+ toggle("git.showDirtyIndicator", " Dirty indicator (*)", {
221
+ hidden: (c) => !c.git.enabled,
222
+ }),
223
+ toggle("git.showChanges", " Line changes (+/-)", {
224
+ hidden: (c) => !c.git.enabled,
225
+ }),
226
+ toggle("git.showStaged", " Staged files", {
227
+ hidden: (c) => !c.git.enabled,
228
+ }),
229
+ toggle("git.showUnstaged", " Unstaged files", {
230
+ hidden: (c) => !c.git.enabled,
231
+ }),
232
+ ],
233
+ },
234
+ {
235
+ id: "global",
236
+ label: "GLOBAL",
237
+ options: [
238
+ cycle("separator", "Separator", SEPARATORS),
239
+ cycle("pathDisplayMode", "Path display", PATH_DISPLAY_MODES),
240
+ toggle("showSonnetModel", "Show Sonnet model"),
241
+ toggle("oneLine", "Single line mode"),
242
+ ],
243
+ },
244
+ ];
245
+
246
+ // ─────────────────────────────────────────────────────────────
247
+ // CONFIG I/O
248
+ // ─────────────────────────────────────────────────────────────
249
+
250
+ function loadConfig(): StatuslineConfig {
251
+ try {
252
+ return JSON.parse(readFileSync(CONFIG_FILE_PATH, "utf-8"));
253
+ } catch {
254
+ return JSON.parse(JSON.stringify(defaultConfig));
255
+ }
256
+ }
257
+
258
+ function saveConfig(config: StatuslineConfig): void {
259
+ writeFileSync(CONFIG_FILE_PATH, JSON.stringify(config, null, 2), "utf-8");
260
+ }
261
+
262
+ // ─────────────────────────────────────────────────────────────
263
+ // RENDERING
264
+ // ─────────────────────────────────────────────────────────────
265
+
266
+ function renderPreview(config: StatuslineConfig): string {
267
+ return renderStatuslineRaw(MOCK_DATA, config);
268
+ }
269
+
270
+ function renderTabs(activeIdx: number): string {
271
+ return tabs
272
+ .map((t, i) =>
273
+ i === activeIdx
274
+ ? colors.yellow(`[${t.label}]`)
275
+ : colors.gray(` ${t.label} `),
276
+ )
277
+ .join("");
278
+ }
279
+
280
+ function renderOptions(
281
+ config: StatuslineConfig,
282
+ tab: Tab,
283
+ selIdx: number,
284
+ ): string {
285
+ const visible = tab.options.filter((o) => !o.hidden || !o.hidden(config));
286
+ return visible
287
+ .map((opt, i) => {
288
+ const sel = i === selIdx;
289
+ const val = opt.getValue?.(config);
290
+ const isPreset = "isPreset" in opt;
291
+
292
+ let display: string;
293
+ if (isPreset) {
294
+ const desc =
295
+ "description" in opt ? colors.gray(` - ${opt.description}`) : "";
296
+ display = `${colors.cyan("◈")} ${opt.label}${desc}`;
297
+ } else if (opt.type === "boolean") {
298
+ display = `${val ? colors.green("●") : colors.gray("○")} ${opt.label}`;
299
+ } else {
300
+ display = `${opt.label}: ${colors.yellow(String(val))}`;
301
+ }
302
+
303
+ const cursor = sel ? colors.yellow("›") : " ";
304
+ return `${cursor} ${sel ? colors.white(display) : colors.lightGray(display)}`;
305
+ })
306
+ .join("\n");
307
+ }
308
+
309
+ function render(
310
+ config: StatuslineConfig,
311
+ tabIdx: number,
312
+ selIdx: number,
313
+ ): string {
314
+ const tab = tabs[tabIdx];
315
+ const preview = renderPreview(config);
316
+ const previewLines = preview.split("\n");
317
+
318
+ return [
319
+ colors.purple("━━━ PREVIEW ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"),
320
+ ...previewLines,
321
+ "",
322
+ renderTabs(tabIdx),
323
+ colors.gray("─".repeat(52)),
324
+ renderOptions(config, tab, selIdx),
325
+ "",
326
+ colors.gray("←→ tabs ↑↓ select Enter toggle s save r reset q quit"),
327
+ ].join("\n");
328
+ }
329
+
330
+ // ─────────────────────────────────────────────────────────────
331
+ // MAIN LOOP
332
+ // ─────────────────────────────────────────────────────────────
333
+
334
+ async function main() {
335
+ let config = loadConfig();
336
+ let tabIdx = 0;
337
+ let selIdx = 0;
338
+
339
+ process.stdout.write("\x1b[?25l");
340
+ process.stdin.setRawMode(true);
341
+ process.stdin.resume();
342
+
343
+ const draw = () => {
344
+ const visible = tabs[tabIdx].options.filter(
345
+ (o) => !o.hidden || !o.hidden(config),
346
+ );
347
+ if (selIdx >= visible.length) selIdx = Math.max(0, visible.length - 1);
348
+ process.stdout.write("\x1b[2J\x1b[H" + render(config, tabIdx, selIdx));
349
+ };
350
+
351
+ draw();
352
+
353
+ for await (const chunk of process.stdin) {
354
+ const key = chunk.toString();
355
+
356
+ if (key === "\u001b[D" || key === "h") {
357
+ tabIdx = (tabIdx - 1 + tabs.length) % tabs.length;
358
+ selIdx = 0;
359
+ } else if (key === "\u001b[C" || key === "l") {
360
+ tabIdx = (tabIdx + 1) % tabs.length;
361
+ selIdx = 0;
362
+ } else if (key === "\u001b[A" || key === "k") {
363
+ if (selIdx > 0) selIdx--;
364
+ } else if (key === "\u001b[B" || key === "j") {
365
+ const visible = tabs[tabIdx].options.filter(
366
+ (o) => !o.hidden || !o.hidden(config),
367
+ );
368
+ if (selIdx < visible.length - 1) selIdx++;
369
+ } else if (key === "\r" || key === " ") {
370
+ const visible = tabs[tabIdx].options.filter(
371
+ (o) => !o.hidden || !o.hidden(config),
372
+ );
373
+ const opt = visible[selIdx];
374
+ if ("isPreset" in opt && "presetIndex" in opt) {
375
+ config = JSON.parse(
376
+ JSON.stringify(PRESETS[opt.presetIndex as number].config),
377
+ );
378
+ } else if (opt.type === "cycle" && opt.cycle) {
379
+ opt.cycle(config, 1);
380
+ } else if (opt.toggle) {
381
+ opt.toggle(config);
382
+ }
383
+ } else if (key.toLowerCase() === "r") {
384
+ config = JSON.parse(JSON.stringify(defaultConfig));
385
+ } else if (key.toLowerCase() === "s") {
386
+ saveConfig(config);
387
+ process.stdout.write("\x1b[2J\x1b[H");
388
+ console.log(colors.green("✓ Config saved!"));
389
+ await Bun.sleep(400);
390
+ } else if (key.toLowerCase() === "q" || key === "\u0003") {
391
+ saveConfig(config);
392
+ process.stdin.setRawMode(false);
393
+ process.stdout.write("\x1b[?25h");
394
+ console.clear();
395
+ console.log(colors.green("✓ Config saved!\n"));
396
+ process.exit(0);
397
+ }
398
+
399
+ draw();
400
+ }
401
+ }
402
+
403
+ main();
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env bun
2
+
3
+ import { readFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { defaultConfig, type StatuslineConfig } from "./lib/config";
6
+ import { getContextData } from "./lib/context";
7
+ import {
8
+ colors,
9
+ formatBranch,
10
+ formatCost,
11
+ formatDuration,
12
+ formatPath,
13
+ } from "./lib/formatters";
14
+ import { getGitStatus } from "./lib/git";
15
+ import {
16
+ renderStatusline,
17
+ type StatuslineData,
18
+ type UsageLimit,
19
+ } from "./lib/render-pure";
20
+ import type { HookInput } from "./lib/types";
21
+
22
+ // Re-export from render-pure for backwards compatibility
23
+ export {
24
+ renderStatusline,
25
+ type StatuslineData,
26
+ type UsageLimit,
27
+ } from "./lib/render-pure";
28
+
29
+ const CONFIG_FILE_PATH = join(import.meta.dir, "..", "statusline.config.json");
30
+ const CLAUDE_SETTINGS_PATH = join(
31
+ process.env.HOME || "",
32
+ ".claude",
33
+ "settings.json",
34
+ );
35
+
36
+ interface ClaudeSettings {
37
+ alwaysThinkingEnabled?: boolean;
38
+ effortLevel?: string;
39
+ }
40
+
41
+ async function loadClaudeSettings(): Promise<ClaudeSettings> {
42
+ try {
43
+ const content = await readFile(CLAUDE_SETTINGS_PATH, "utf-8");
44
+ return JSON.parse(content);
45
+ } catch {
46
+ return {};
47
+ }
48
+ }
49
+
50
+ async function loadConfig(): Promise<StatuslineConfig> {
51
+ try {
52
+ const content = await readFile(CONFIG_FILE_PATH, "utf-8");
53
+ return JSON.parse(content);
54
+ } catch {
55
+ return defaultConfig;
56
+ }
57
+ }
58
+
59
+ async function main() {
60
+ try {
61
+ const input: HookInput = await Bun.stdin.json();
62
+
63
+ const config = await loadConfig();
64
+ const claudeSettings = await loadClaudeSettings();
65
+
66
+ const git = await getGitStatus();
67
+
68
+ let contextTokens: number | null;
69
+ let contextPercentage: number | null;
70
+
71
+ const usePayloadContext =
72
+ config.context.usePayloadContextWindow && input.context_window;
73
+
74
+ if (usePayloadContext) {
75
+ const current = input.context_window?.current_usage;
76
+ if (current) {
77
+ contextTokens =
78
+ (current.input_tokens || 0) +
79
+ (current.cache_creation_input_tokens || 0) +
80
+ (current.cache_read_input_tokens || 0);
81
+ const maxTokens =
82
+ input.context_window?.context_window_size ||
83
+ config.context.maxContextTokens;
84
+ contextPercentage = Math.min(
85
+ 100,
86
+ Math.round((contextTokens / maxTokens) * 100),
87
+ );
88
+ } else {
89
+ // No context data yet - session not started
90
+ contextTokens = null;
91
+ contextPercentage = null;
92
+ }
93
+ } else {
94
+ const contextData = await getContextData({
95
+ transcriptPath: input.transcript_path,
96
+ maxContextTokens: config.context.maxContextTokens,
97
+ autocompactBufferTokens: config.context.autocompactBufferTokens,
98
+ useUsableContextOnly: config.context.useUsableContextOnly,
99
+ overheadTokens: config.context.overheadTokens,
100
+ });
101
+ contextTokens = contextData.tokens;
102
+ contextPercentage = contextData.percentage;
103
+ }
104
+
105
+ const data: StatuslineData = {
106
+ branch: formatBranch(git, config.git),
107
+ dirPath: formatPath(input.workspace.current_dir, config.pathDisplayMode),
108
+ modelName: (() => {
109
+ const shortName = input.model.display_name.replace(
110
+ /\s*\((\d+[KM])\s+context\)/i,
111
+ " $1",
112
+ );
113
+ return claudeSettings.effortLevel
114
+ ? `${shortName} [${claudeSettings.effortLevel}]`
115
+ : shortName;
116
+ })(),
117
+ sessionCost: formatCost(
118
+ input.cost.total_cost_usd,
119
+ config.session.cost.format,
120
+ ),
121
+ sessionDuration: formatDuration(input.cost.total_duration_ms),
122
+ contextTokens,
123
+ contextPercentage,
124
+ thinkingEnabled: claudeSettings.alwaysThinkingEnabled ?? true,
125
+ };
126
+
127
+ const output = renderStatusline(data, config);
128
+ console.log(output);
129
+ if (config.oneLine) {
130
+ console.log("");
131
+ }
132
+ } catch (error) {
133
+ const errorMessage = error instanceof Error ? error.message : String(error);
134
+ console.log(`${colors.red("Error:")} ${errorMessage}`);
135
+ console.log(colors.gray("Check statusline configuration"));
136
+ }
137
+ }
138
+
139
+ if (import.meta.main) {
140
+ main();
141
+ }
@@ -0,0 +1,110 @@
1
+ export type Separator =
2
+ | "|"
3
+ | "•"
4
+ | "·"
5
+ | "⋅"
6
+ | "●"
7
+ | "◆"
8
+ | "▪"
9
+ | "▸"
10
+ | "›"
11
+ | "→";
12
+
13
+ export type CostFormat = "integer" | "decimal1" | "decimal2";
14
+ export type ProgressBarStyle = "filled" | "rectangle" | "braille";
15
+ export type ProgressBarColor =
16
+ | "progressive"
17
+ | "green"
18
+ | "yellow"
19
+ | "red"
20
+ | "peach"
21
+ | "black"
22
+ | "white"
23
+ | "purple"
24
+ | "blue"
25
+ | "cyan";
26
+ export type ProgressBarBackground =
27
+ | "none"
28
+ | "dark"
29
+ | "gray"
30
+ | "light"
31
+ | "blue"
32
+ | "purple"
33
+ | "cyan"
34
+ | "peach";
35
+
36
+ export interface CostConfig {
37
+ enabled: boolean;
38
+ format: CostFormat;
39
+ }
40
+
41
+ export interface ProgressBarConfig {
42
+ enabled: boolean;
43
+ length: 5 | 10 | 15;
44
+ style: ProgressBarStyle;
45
+ color: ProgressBarColor;
46
+ background: ProgressBarBackground;
47
+ }
48
+
49
+ export interface PercentageConfig {
50
+ enabled: boolean;
51
+ showValue: boolean;
52
+ progressBar: ProgressBarConfig;
53
+ }
54
+
55
+ export interface StatuslineConfig {
56
+ features?: {
57
+ usageLimits?: boolean;
58
+ spendTracking?: boolean;
59
+ };
60
+ oneLine: boolean;
61
+ showSonnetModel: boolean;
62
+ pathDisplayMode: "full" | "truncated" | "basename";
63
+ git: {
64
+ enabled: boolean;
65
+ showBranch: boolean;
66
+ showDirtyIndicator: boolean;
67
+ showChanges: boolean;
68
+ showStaged: boolean;
69
+ showUnstaged: boolean;
70
+ };
71
+ separator: Separator;
72
+ session: {
73
+ infoSeparator: Separator | null;
74
+ cost: CostConfig;
75
+ duration: { enabled: boolean };
76
+ tokens: {
77
+ enabled: boolean;
78
+ showMax: boolean;
79
+ showDecimals: boolean;
80
+ };
81
+ percentage: PercentageConfig;
82
+ };
83
+ context: {
84
+ usePayloadContextWindow: boolean;
85
+ maxContextTokens: number;
86
+ autocompactBufferTokens: number;
87
+ useUsableContextOnly: boolean;
88
+ overheadTokens: number;
89
+ };
90
+ limits: {
91
+ enabled: boolean;
92
+ showTimeLeft: boolean;
93
+ showPacingDelta: boolean;
94
+ cost: CostConfig;
95
+ percentage: PercentageConfig;
96
+ };
97
+ weeklyUsage: {
98
+ enabled: boolean | "90%";
99
+ showTimeLeft: boolean;
100
+ showPacingDelta: boolean;
101
+ cost: CostConfig;
102
+ percentage: PercentageConfig;
103
+ };
104
+ dailySpend: {
105
+ cost: CostConfig;
106
+ };
107
+ thinking: {
108
+ showDisabledWarning: boolean;
109
+ };
110
+ }
@@ -0,0 +1,21 @@
1
+ import { readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import type { StatuslineConfig } from "./config-types";
4
+
5
+ const CONFIG_DIR = join(import.meta.dir, "..", "..");
6
+ const DEFAULTS_PATH = join(CONFIG_DIR, "defaults.json");
7
+ const CONFIG_PATH = join(CONFIG_DIR, "statusline.config.json");
8
+
9
+ export const defaultConfig: StatuslineConfig = JSON.parse(
10
+ readFileSync(DEFAULTS_PATH, "utf-8"),
11
+ );
12
+
13
+ export function loadConfig(): StatuslineConfig {
14
+ try {
15
+ return JSON.parse(readFileSync(CONFIG_PATH, "utf-8"));
16
+ } catch {
17
+ return JSON.parse(JSON.stringify(defaultConfig));
18
+ }
19
+ }
20
+
21
+ export type { StatuslineConfig } from "./config-types";