@npow/oh-my-claude 0.1.0
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/README.md +317 -0
- package/bin/omc.js +403 -0
- package/docs/architecture.md +198 -0
- package/docs/segment-contract.md +186 -0
- package/docs/theme-format.md +156 -0
- package/package.json +35 -0
- package/src/cache.js +102 -0
- package/src/color.js +105 -0
- package/src/compositor.js +163 -0
- package/src/config.js +146 -0
- package/src/plugins.js +72 -0
- package/src/runner.js +160 -0
- package/src/segments/achievement.js +68 -0
- package/src/segments/api-timer.js +55 -0
- package/src/segments/battle-log.js +55 -0
- package/src/segments/cat.js +89 -0
- package/src/segments/coffee-cup.js +81 -0
- package/src/segments/commit-msg.js +95 -0
- package/src/segments/context-bar.js +50 -0
- package/src/segments/context-percent.js +40 -0
- package/src/segments/context-tokens.js +52 -0
- package/src/segments/cost-budget.js +43 -0
- package/src/segments/coworker.js +137 -0
- package/src/segments/custom-text.js +25 -0
- package/src/segments/directory.js +75 -0
- package/src/segments/emoji-story.js +99 -0
- package/src/segments/flex-space.js +25 -0
- package/src/segments/fortune-cookie.js +131 -0
- package/src/segments/garden.js +57 -0
- package/src/segments/git-branch.js +36 -0
- package/src/segments/git-status.js +56 -0
- package/src/segments/horoscope.js +134 -0
- package/src/segments/index.js +65 -0
- package/src/segments/lines-changed.js +29 -0
- package/src/segments/model-name.js +28 -0
- package/src/segments/narrator.js +129 -0
- package/src/segments/output-style.js +25 -0
- package/src/segments/rpg-stats.js +119 -0
- package/src/segments/separator-arrow.js +22 -0
- package/src/segments/separator-pipe.js +22 -0
- package/src/segments/separator-space.js +22 -0
- package/src/segments/session-cost.js +72 -0
- package/src/segments/session-timer.js +53 -0
- package/src/segments/smart-nudge.js +97 -0
- package/src/segments/soundtrack.js +133 -0
- package/src/segments/speedrun.js +94 -0
- package/src/segments/stock-ticker.js +71 -0
- package/src/segments/streak.js +131 -0
- package/src/segments/tamagotchi.js +95 -0
- package/src/segments/token-sparkline.js +73 -0
- package/src/segments/version.js +27 -0
- package/src/segments/vibe-check.js +109 -0
- package/src/segments/vim-mode.js +29 -0
- package/src/segments/weather-report.js +88 -0
- package/themes/default.json +59 -0
- package/themes/minimal.json +37 -0
- package/themes/powerline.json +73 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// src/segments/vibe-check.js — Session mood indicator derived from metrics
|
|
2
|
+
// Zero dependencies. Node 18+ ESM.
|
|
3
|
+
|
|
4
|
+
export const meta = {
|
|
5
|
+
name: 'vibe-check',
|
|
6
|
+
description: 'Shows a one-word mood indicator derived from session metrics',
|
|
7
|
+
requires: [],
|
|
8
|
+
defaultConfig: {
|
|
9
|
+
style: '',
|
|
10
|
+
showEmoji: true,
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Vibes in priority order (highest wins). Each entry:
|
|
16
|
+
* test(data) — returns true when this vibe applies
|
|
17
|
+
* emoji — emoji prefix
|
|
18
|
+
* label — text label
|
|
19
|
+
* style — ANSI style string
|
|
20
|
+
*/
|
|
21
|
+
const VIBES = [
|
|
22
|
+
{
|
|
23
|
+
test: (d) => (d?.context_window?.used_percentage ?? 0) >= 80,
|
|
24
|
+
emoji: '\u{1F9D8}',
|
|
25
|
+
label: 'time to compact',
|
|
26
|
+
style: 'bold red',
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
test: (d) => (d?.context_window?.used_percentage ?? 0) >= 65,
|
|
30
|
+
emoji: '\u{1F630}',
|
|
31
|
+
label: 'sweating',
|
|
32
|
+
style: 'bold yellow',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
test: (d) => (d?.cost?.total_cost_usd ?? 0) >= 15,
|
|
36
|
+
emoji: '\u{1F4B8}',
|
|
37
|
+
label: 'burning cash',
|
|
38
|
+
style: 'bold magenta',
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
test: (d) =>
|
|
42
|
+
(d?.cost?.total_lines_added ?? 0) >= 100 &&
|
|
43
|
+
(d?.context_window?.used_percentage ?? 0) >= 40,
|
|
44
|
+
emoji: '\u{1F525}',
|
|
45
|
+
label: 'cooking',
|
|
46
|
+
style: 'bold yellow',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
test: (d) =>
|
|
50
|
+
(d?.cost?.total_duration_ms ?? 0) >= 1_800_000 &&
|
|
51
|
+
(d?.context_window?.used_percentage ?? 0) < 60,
|
|
52
|
+
emoji: '\u{1F3AF}',
|
|
53
|
+
label: 'locked in',
|
|
54
|
+
style: 'cyan',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
test: (d) => (d?.cost?.total_lines_added ?? 0) >= 200,
|
|
58
|
+
emoji: '\u{1F680}',
|
|
59
|
+
label: 'shipping',
|
|
60
|
+
style: 'bold green',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
test: (d) => (d?.cost?.total_lines_added ?? 0) >= 50,
|
|
64
|
+
emoji: '\u{1F3D7}\uFE0F',
|
|
65
|
+
label: 'building',
|
|
66
|
+
style: 'green',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
test: (d) =>
|
|
70
|
+
(d?.cost?.total_lines_added ?? 0) < 10 &&
|
|
71
|
+
(d?.cost?.total_duration_ms ?? 0) >= 300_000,
|
|
72
|
+
emoji: '\u{1F50D}',
|
|
73
|
+
label: 'exploring',
|
|
74
|
+
style: 'blue',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
test: (d) => (d?.cost?.total_duration_ms ?? 0) < 120_000,
|
|
78
|
+
emoji: '\u2615',
|
|
79
|
+
label: 'warming up',
|
|
80
|
+
style: 'dim',
|
|
81
|
+
},
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
const DEFAULT_VIBE = {
|
|
85
|
+
emoji: '\u2728',
|
|
86
|
+
label: 'vibing',
|
|
87
|
+
style: 'cyan',
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* @param {object} data - Parsed stdin JSON from Claude Code
|
|
92
|
+
* @param {object} config - Per-segment config from theme
|
|
93
|
+
* @returns {{text: string, style: string}}
|
|
94
|
+
*/
|
|
95
|
+
export function render(data, config) {
|
|
96
|
+
const cfg = { ...meta.defaultConfig, ...config };
|
|
97
|
+
|
|
98
|
+
// Find the first matching vibe (priority order), or fall back to default
|
|
99
|
+
const vibe = VIBES.find((v) => v.test(data)) ?? DEFAULT_VIBE;
|
|
100
|
+
|
|
101
|
+
const text = cfg.showEmoji
|
|
102
|
+
? `${vibe.emoji} ${vibe.label}`
|
|
103
|
+
: vibe.label;
|
|
104
|
+
|
|
105
|
+
// Allow per-segment style override; fall back to vibe-specific style
|
|
106
|
+
const style = cfg.style || vibe.style;
|
|
107
|
+
|
|
108
|
+
return { text, style };
|
|
109
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// src/segments/vim-mode.js — Display current vim mode
|
|
2
|
+
// Zero dependencies. Node 18+ ESM.
|
|
3
|
+
|
|
4
|
+
export const meta = {
|
|
5
|
+
name: 'vim-mode',
|
|
6
|
+
description: 'Shows the current vim mode (NORMAL/INSERT) when vim is active',
|
|
7
|
+
requires: [],
|
|
8
|
+
defaultConfig: {
|
|
9
|
+
styleNormal: 'bold green',
|
|
10
|
+
styleInsert: 'bold blue',
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @param {object} data - Parsed stdin JSON from Claude Code
|
|
16
|
+
* @param {object} config - Per-segment config from theme
|
|
17
|
+
* @returns {{text: string, style: string}|null}
|
|
18
|
+
*/
|
|
19
|
+
export function render(data, config) {
|
|
20
|
+
const cfg = { ...meta.defaultConfig, ...config };
|
|
21
|
+
|
|
22
|
+
const mode = data?.vim?.mode;
|
|
23
|
+
if (!mode) return null;
|
|
24
|
+
|
|
25
|
+
const upper = mode.toUpperCase();
|
|
26
|
+
const style = upper === 'INSERT' ? cfg.styleInsert : cfg.styleNormal;
|
|
27
|
+
|
|
28
|
+
return { text: upper, style };
|
|
29
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
// src/segments/weather-report.js — Session conditions as weather forecast
|
|
2
|
+
// Zero dependencies. Node 18+ ESM.
|
|
3
|
+
//
|
|
4
|
+
// Conditions (priority order):
|
|
5
|
+
// 1. Stormy — context >= 85%
|
|
6
|
+
// 2. Overcast — context >= 65%
|
|
7
|
+
// 3. Hail — cost > $15
|
|
8
|
+
// 4. Windy — lines_added > 200
|
|
9
|
+
// 5. Turbulent — lines_removed > lines_added
|
|
10
|
+
// 6. Partly Cloudy — duration > 1 hour
|
|
11
|
+
// 7. Dawn — duration < 2 minutes
|
|
12
|
+
// 8. Clear Skies — default
|
|
13
|
+
|
|
14
|
+
export const meta = {
|
|
15
|
+
name: 'weather-report',
|
|
16
|
+
description: 'Session conditions as a weather forecast',
|
|
17
|
+
requires: [],
|
|
18
|
+
defaultConfig: {
|
|
19
|
+
style: '',
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Determine the weather condition from session metrics.
|
|
25
|
+
* Priority order is enforced by early returns.
|
|
26
|
+
*
|
|
27
|
+
* @param {object} data - Parsed stdin JSON from Claude Code
|
|
28
|
+
* @returns {{ text: string, style: string }}
|
|
29
|
+
*/
|
|
30
|
+
function resolveWeather(data) {
|
|
31
|
+
const context = data?.context_window?.used_percentage ?? 0;
|
|
32
|
+
const cost = data?.cost?.total_cost_usd ?? 0;
|
|
33
|
+
const linesAdded = data?.cost?.total_lines_added ?? 0;
|
|
34
|
+
const linesRemoved = data?.cost?.total_lines_removed ?? 0;
|
|
35
|
+
const totalDuration = data?.cost?.total_duration_ms ?? 0;
|
|
36
|
+
|
|
37
|
+
// 1. Stormy
|
|
38
|
+
if (context >= 85) {
|
|
39
|
+
return { text: '\u26C8\uFE0F Stormy', style: 'bold red' };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// 2. Overcast
|
|
43
|
+
if (context >= 65) {
|
|
44
|
+
return { text: '\uD83C\uDF27\uFE0F Overcast', style: 'yellow' };
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// 3. Hail
|
|
48
|
+
if (cost > 15) {
|
|
49
|
+
return { text: `\uD83C\uDF28\uFE0F Hail ($${cost.toFixed(2)})`, style: 'bold yellow' };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 4. Windy
|
|
53
|
+
if (linesAdded > 200) {
|
|
54
|
+
return { text: `\uD83D\uDCA8 Windy (+${linesAdded})`, style: 'cyan' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// 5. Turbulent
|
|
58
|
+
if (linesRemoved > linesAdded) {
|
|
59
|
+
return { text: '\uD83C\uDF2A\uFE0F Turbulent', style: 'yellow' };
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// 6. Partly Cloudy
|
|
63
|
+
if (totalDuration > 3_600_000) {
|
|
64
|
+
return { text: '\uD83C\uDF24\uFE0F Partly Cloudy', style: '' };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// 7. Dawn
|
|
68
|
+
if (totalDuration < 120_000) {
|
|
69
|
+
return { text: '\uD83C\uDF05 Dawn', style: 'dim' };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// 8. Clear Skies
|
|
73
|
+
return { text: '\u2600\uFE0F Clear Skies', style: 'green' };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {object} data - Parsed stdin JSON from Claude Code
|
|
78
|
+
* @param {object} config - Per-segment config from theme
|
|
79
|
+
* @returns {{text: string, style: string}}
|
|
80
|
+
*/
|
|
81
|
+
export function render(data, config) {
|
|
82
|
+
const cfg = { ...meta.defaultConfig, ...config };
|
|
83
|
+
|
|
84
|
+
const weather = resolveWeather(data);
|
|
85
|
+
const style = cfg.style || weather.style;
|
|
86
|
+
|
|
87
|
+
return { text: weather.text, style };
|
|
88
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "default",
|
|
3
|
+
"description": "Clean two-line statusline. No special fonts required. Works everywhere.",
|
|
4
|
+
"lines": [
|
|
5
|
+
{
|
|
6
|
+
"left": ["model-name", "directory", "git-branch", "git-status"],
|
|
7
|
+
"right": ["lines-changed", "session-timer"]
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"left": ["context-bar"],
|
|
11
|
+
"right": ["session-cost"]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"separator": " ",
|
|
15
|
+
"segments": {
|
|
16
|
+
"model-name": {
|
|
17
|
+
"style": "bold cyan",
|
|
18
|
+
"icon": false
|
|
19
|
+
},
|
|
20
|
+
"directory": {
|
|
21
|
+
"style": "white",
|
|
22
|
+
"format": "basename"
|
|
23
|
+
},
|
|
24
|
+
"git-branch": {
|
|
25
|
+
"style": "green",
|
|
26
|
+
"icon": false,
|
|
27
|
+
"maxLength": 30
|
|
28
|
+
},
|
|
29
|
+
"git-status": {
|
|
30
|
+
"style": "yellow",
|
|
31
|
+
"format": "short"
|
|
32
|
+
},
|
|
33
|
+
"lines-changed": {
|
|
34
|
+
"style": "dim",
|
|
35
|
+
"hideIfZero": true
|
|
36
|
+
},
|
|
37
|
+
"session-timer": {
|
|
38
|
+
"style": "dim"
|
|
39
|
+
},
|
|
40
|
+
"context-bar": {
|
|
41
|
+
"width": 20,
|
|
42
|
+
"charFilled": "\u2593",
|
|
43
|
+
"charEmpty": "\u2591",
|
|
44
|
+
"warnAt": 60,
|
|
45
|
+
"criticalAt": 80,
|
|
46
|
+
"showPercent": true,
|
|
47
|
+
"styleOk": "green",
|
|
48
|
+
"styleWarn": "bold yellow",
|
|
49
|
+
"styleCritical": "bold red"
|
|
50
|
+
},
|
|
51
|
+
"session-cost": {
|
|
52
|
+
"style": "dim",
|
|
53
|
+
"format": "$",
|
|
54
|
+
"plan": null,
|
|
55
|
+
"styleWarn": "bold yellow",
|
|
56
|
+
"styleCritical": "bold red"
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "minimal",
|
|
3
|
+
"description": "Single-line, text-only. No bars, no icons, no fuss.",
|
|
4
|
+
"lines": [
|
|
5
|
+
{
|
|
6
|
+
"left": ["directory", "git-branch", "git-status"],
|
|
7
|
+
"right": ["context-percent", "session-cost"]
|
|
8
|
+
}
|
|
9
|
+
],
|
|
10
|
+
"separator": " \u00b7 ",
|
|
11
|
+
"segments": {
|
|
12
|
+
"directory": {
|
|
13
|
+
"style": "white",
|
|
14
|
+
"format": "basename"
|
|
15
|
+
},
|
|
16
|
+
"git-branch": {
|
|
17
|
+
"style": "green",
|
|
18
|
+
"icon": false,
|
|
19
|
+
"maxLength": 20
|
|
20
|
+
},
|
|
21
|
+
"git-status": {
|
|
22
|
+
"style": "yellow",
|
|
23
|
+
"format": "short"
|
|
24
|
+
},
|
|
25
|
+
"context-percent": {
|
|
26
|
+
"style": "white",
|
|
27
|
+
"warnAt": 60,
|
|
28
|
+
"criticalAt": 80,
|
|
29
|
+
"styleWarn": "yellow",
|
|
30
|
+
"styleCritical": "red"
|
|
31
|
+
},
|
|
32
|
+
"session-cost": {
|
|
33
|
+
"style": "dim",
|
|
34
|
+
"format": "$"
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "powerline",
|
|
3
|
+
"description": "Full powerline with Nerd Font icons and arrow separators. Requires a Nerd Font.",
|
|
4
|
+
"lines": [
|
|
5
|
+
{
|
|
6
|
+
"left": ["model-name", "separator-arrow", "directory", "separator-arrow", "git-branch", "git-status"],
|
|
7
|
+
"right": ["lines-changed", "separator-arrow", "session-timer"]
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
"left": ["context-bar", "separator-arrow", "context-tokens"],
|
|
11
|
+
"right": ["session-cost", "separator-arrow", "vim-mode"]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"separator": " ",
|
|
15
|
+
"segments": {
|
|
16
|
+
"model-name": {
|
|
17
|
+
"style": "bold cyan",
|
|
18
|
+
"icon": true
|
|
19
|
+
},
|
|
20
|
+
"directory": {
|
|
21
|
+
"style": "blue",
|
|
22
|
+
"format": "fish",
|
|
23
|
+
"icon": true
|
|
24
|
+
},
|
|
25
|
+
"git-branch": {
|
|
26
|
+
"style": "green",
|
|
27
|
+
"icon": true,
|
|
28
|
+
"maxLength": 25
|
|
29
|
+
},
|
|
30
|
+
"git-status": {
|
|
31
|
+
"style": "yellow",
|
|
32
|
+
"format": "detailed"
|
|
33
|
+
},
|
|
34
|
+
"lines-changed": {
|
|
35
|
+
"style": "dim",
|
|
36
|
+
"hideIfZero": true
|
|
37
|
+
},
|
|
38
|
+
"session-timer": {
|
|
39
|
+
"style": "dim",
|
|
40
|
+
"icon": true
|
|
41
|
+
},
|
|
42
|
+
"context-bar": {
|
|
43
|
+
"width": 15,
|
|
44
|
+
"charFilled": "\u2588",
|
|
45
|
+
"charEmpty": "\u2591",
|
|
46
|
+
"warnAt": 60,
|
|
47
|
+
"criticalAt": 80,
|
|
48
|
+
"showPercent": true,
|
|
49
|
+
"styleOk": "green",
|
|
50
|
+
"styleWarn": "yellow",
|
|
51
|
+
"styleCritical": "red"
|
|
52
|
+
},
|
|
53
|
+
"context-tokens": {
|
|
54
|
+
"style": "dim",
|
|
55
|
+
"format": "short"
|
|
56
|
+
},
|
|
57
|
+
"session-cost": {
|
|
58
|
+
"style": "white",
|
|
59
|
+
"format": "$",
|
|
60
|
+
"warnAt": 5.0,
|
|
61
|
+
"styleWarn": "yellow",
|
|
62
|
+
"icon": true
|
|
63
|
+
},
|
|
64
|
+
"vim-mode": {
|
|
65
|
+
"styleNormal": "bold green",
|
|
66
|
+
"styleInsert": "bold blue"
|
|
67
|
+
},
|
|
68
|
+
"separator-arrow": {
|
|
69
|
+
"style": "dim",
|
|
70
|
+
"char": "\ue0b0"
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|