@nutthead/cc-statusline 0.1.9 → 0.2.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 +11 -0
- package/bin/cc-statusline.js +9466 -34
- package/index.ts +31 -2
- package/package.json +3 -1
- package/src/cli.ts +78 -40
- package/src/{theme/default.ts → defaultTheme.ts} +11 -6
- package/src/theme/loadTheme.ts +35 -0
package/index.ts
CHANGED
|
@@ -1,9 +1,38 @@
|
|
|
1
|
+
import meow from "meow";
|
|
1
2
|
import { configure } from "@logtape/logtape";
|
|
2
3
|
import { log, logtapeConfig } from "./src/logging";
|
|
3
|
-
import { defaultTheme } from "./src/
|
|
4
|
+
import { defaultTheme } from "./src/defaultTheme";
|
|
5
|
+
import { loadTheme } from "./src/theme/loadTheme";
|
|
4
6
|
|
|
5
7
|
await configure(logtapeConfig);
|
|
6
8
|
|
|
9
|
+
const cli = meow(
|
|
10
|
+
`
|
|
11
|
+
Usage
|
|
12
|
+
$ cc-statusline
|
|
13
|
+
|
|
14
|
+
Options
|
|
15
|
+
--theme, -t Use a custom theme
|
|
16
|
+
|
|
17
|
+
Examples
|
|
18
|
+
$ cc-statusline --rainbow
|
|
19
|
+
$ cc-statusline --theme ~/.config/cc-statusline/basic.js
|
|
20
|
+
|
|
21
|
+
`,
|
|
22
|
+
{
|
|
23
|
+
importMeta: import.meta, // This is required
|
|
24
|
+
flags: {
|
|
25
|
+
theme: {
|
|
26
|
+
type: "string",
|
|
27
|
+
shortFlag: "t",
|
|
28
|
+
isRequired: false,
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
const resolvedTheme = (cli.flags.theme && await loadTheme(cli.flags.theme)) || defaultTheme;
|
|
35
|
+
|
|
7
36
|
const input = await Bun.stdin.stream().json();
|
|
8
37
|
log.debug("input: {input}", input);
|
|
9
|
-
console.log(await
|
|
38
|
+
console.log(await resolvedTheme(input));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nutthead/cc-statusline",
|
|
3
3
|
"description": "Status Line for Claude Code",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.2.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"cc-statusline": "bin/cc-statusline.js"
|
|
@@ -42,7 +42,9 @@
|
|
|
42
42
|
"@logtape/file": "^1.3.6",
|
|
43
43
|
"@logtape/logtape": "^1.3.6",
|
|
44
44
|
"ansi-colors": "^4.1.3",
|
|
45
|
+
"meow": "^14.0.0",
|
|
45
46
|
"simple-git": "^3.30.0",
|
|
47
|
+
"ts-pattern": "^5.9.0",
|
|
46
48
|
"type-fest": "^5.3.1",
|
|
47
49
|
"zod": "^4.3.5"
|
|
48
50
|
},
|
package/src/cli.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
+
import meow from "meow";
|
|
3
4
|
import { execSync } from "node:child_process";
|
|
4
5
|
import { existsSync, mkdirSync, copyFileSync, unlinkSync } from "node:fs";
|
|
5
6
|
import { homedir } from "node:os";
|
|
@@ -9,70 +10,107 @@ const BINARY_NAME = "statusline";
|
|
|
9
10
|
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
10
11
|
const TARGET_PATH = join(CLAUDE_DIR, BINARY_NAME);
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
const cli = meow(
|
|
14
|
+
`
|
|
15
|
+
Usage
|
|
16
|
+
$ cc-statusline <command>
|
|
17
|
+
|
|
18
|
+
Commands
|
|
19
|
+
install Build and install statusline to ~/.claude/
|
|
20
|
+
|
|
21
|
+
Options
|
|
22
|
+
--overwrite Overwrite existing file if it exists
|
|
23
|
+
|
|
24
|
+
Examples
|
|
25
|
+
$ cc-statusline install
|
|
26
|
+
$ cc-statusline install --overwrite
|
|
27
|
+
`,
|
|
28
|
+
{
|
|
29
|
+
importMeta: import.meta,
|
|
30
|
+
flags: {
|
|
31
|
+
overwrite: {
|
|
32
|
+
type: "boolean",
|
|
33
|
+
default: false,
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
export function build(): void {
|
|
13
40
|
console.log("Building statusline binary...");
|
|
14
|
-
execSync(
|
|
15
|
-
|
|
16
|
-
|
|
41
|
+
execSync(
|
|
42
|
+
"mkdir -p target && bun build --compile ./index.ts --outfile target/statusline",
|
|
43
|
+
{ stdio: "inherit" },
|
|
44
|
+
);
|
|
17
45
|
console.log("Build complete.");
|
|
18
46
|
}
|
|
19
47
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
48
|
+
export interface InstallDeps {
|
|
49
|
+
claudeDir: string;
|
|
50
|
+
targetPath: string;
|
|
51
|
+
sourcePath: string;
|
|
52
|
+
doBuild: () => void;
|
|
53
|
+
existsSync: (path: string) => boolean;
|
|
54
|
+
mkdirSync: (path: string, options?: { recursive?: boolean }) => void;
|
|
55
|
+
copyFileSync: (src: string, dest: string) => void;
|
|
56
|
+
unlinkSync: (path: string) => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const defaultDeps: InstallDeps = {
|
|
60
|
+
claudeDir: CLAUDE_DIR,
|
|
61
|
+
targetPath: TARGET_PATH,
|
|
62
|
+
sourcePath: join(process.cwd(), "target", BINARY_NAME),
|
|
63
|
+
doBuild: build,
|
|
64
|
+
existsSync,
|
|
65
|
+
mkdirSync,
|
|
66
|
+
copyFileSync,
|
|
67
|
+
unlinkSync,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export function installBinary(
|
|
71
|
+
overwrite: boolean,
|
|
72
|
+
deps: InstallDeps = defaultDeps,
|
|
73
|
+
): void {
|
|
74
|
+
deps.doBuild();
|
|
23
75
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
mkdirSync(CLAUDE_DIR, { recursive: true });
|
|
76
|
+
if (!deps.existsSync(deps.claudeDir)) {
|
|
77
|
+
deps.mkdirSync(deps.claudeDir, { recursive: true });
|
|
27
78
|
}
|
|
28
79
|
|
|
29
|
-
|
|
30
|
-
if (existsSync(TARGET_PATH)) {
|
|
80
|
+
if (deps.existsSync(deps.targetPath)) {
|
|
31
81
|
if (!overwrite) {
|
|
32
|
-
console.error(`Error: ${
|
|
82
|
+
console.error(`Error: ${deps.targetPath} already exists.`);
|
|
33
83
|
console.error("Use --overwrite to replace the existing file.");
|
|
34
84
|
process.exit(1);
|
|
35
85
|
}
|
|
36
|
-
console.log(`Overwriting existing file at ${
|
|
37
|
-
unlinkSync(
|
|
86
|
+
console.log(`Overwriting existing file at ${deps.targetPath}...`);
|
|
87
|
+
deps.unlinkSync(deps.targetPath);
|
|
38
88
|
}
|
|
39
89
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
copyFileSync(sourcePath, TARGET_PATH);
|
|
43
|
-
console.log(`Installed statusline to ${TARGET_PATH}`);
|
|
90
|
+
deps.copyFileSync(deps.sourcePath, deps.targetPath);
|
|
91
|
+
console.log(`Installed statusline to ${deps.targetPath}`);
|
|
44
92
|
}
|
|
45
93
|
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Commands:
|
|
50
|
-
install Build and install statusline to ~/.claude/
|
|
51
|
-
|
|
52
|
-
Options:
|
|
53
|
-
--overwrite Overwrite existing file if it exists
|
|
54
|
-
--help, -h Show this help message`);
|
|
94
|
+
export function install(overwrite: boolean): void {
|
|
95
|
+
installBinary(overwrite);
|
|
55
96
|
}
|
|
56
97
|
|
|
57
98
|
function main(): void {
|
|
58
|
-
const
|
|
59
|
-
const command = args.find((arg) => !arg.startsWith("-"));
|
|
60
|
-
const flags = new Set(args.filter((arg) => arg.startsWith("-")));
|
|
61
|
-
|
|
62
|
-
if (flags.has("--help") || flags.has("-h") || args.length === 0) {
|
|
63
|
-
printUsage();
|
|
64
|
-
process.exit(0);
|
|
65
|
-
}
|
|
99
|
+
const command = cli.input[0];
|
|
66
100
|
|
|
67
101
|
switch (command) {
|
|
68
102
|
case "install":
|
|
69
|
-
install(flags.
|
|
103
|
+
install(cli.flags.overwrite);
|
|
104
|
+
break;
|
|
105
|
+
case undefined:
|
|
106
|
+
cli.showHelp();
|
|
70
107
|
break;
|
|
71
108
|
default:
|
|
72
109
|
console.error(`Unknown command: ${command}`);
|
|
73
|
-
|
|
74
|
-
process.exit(1);
|
|
110
|
+
cli.showHelp(1);
|
|
75
111
|
}
|
|
76
112
|
}
|
|
77
113
|
|
|
78
|
-
main
|
|
114
|
+
if (import.meta.main) {
|
|
115
|
+
main();
|
|
116
|
+
}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { log } from "
|
|
3
|
-
import { statusSchema } from "
|
|
2
|
+
import { log } from "./logging";
|
|
3
|
+
import { statusSchema } from "./statusLineSchema";
|
|
4
4
|
import {
|
|
5
5
|
currentDirStatus,
|
|
6
6
|
currentGitStatus,
|
|
7
7
|
currentModelStatus,
|
|
8
8
|
currentSessionId,
|
|
9
|
-
} from "
|
|
9
|
+
} from "./utils";
|
|
10
10
|
|
|
11
11
|
import c from "ansi-colors";
|
|
12
12
|
|
|
13
|
-
async function defaultTheme(input?: string) {
|
|
13
|
+
async function defaultTheme(input?: string): Promise<string> {
|
|
14
14
|
let statusLine = null;
|
|
15
15
|
|
|
16
16
|
if (input) {
|
|
@@ -22,9 +22,14 @@ async function defaultTheme(input?: string) {
|
|
|
22
22
|
const gitStatus = c.green(await currentGitStatus());
|
|
23
23
|
const modelStatus = c.magenta(currentModelStatus(status));
|
|
24
24
|
const sessionId = c.blue(currentSessionId(status));
|
|
25
|
-
const separator = c.bold.gray("⋮");
|
|
25
|
+
const separator = c.bold.gray(" ⋮ ");
|
|
26
26
|
|
|
27
|
-
statusLine =
|
|
27
|
+
statusLine = [
|
|
28
|
+
[dirStatus, gitStatus],
|
|
29
|
+
[modelStatus, sessionId],
|
|
30
|
+
]
|
|
31
|
+
.map((row) => row.join(separator))
|
|
32
|
+
.join("\n");
|
|
28
33
|
} else {
|
|
29
34
|
log.error("Failed to parse input: {error}", {
|
|
30
35
|
error: JSON.stringify(z.treeifyError(result.error)),
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { join, resolve } from "node:path";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
|
|
4
|
+
type ThemeFunction = (input?: string) => Promise<string>;
|
|
5
|
+
|
|
6
|
+
interface ThemeModule {
|
|
7
|
+
default: ThemeFunction;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function expandTilde(path: string) {
|
|
11
|
+
if (path.startsWith("~")) {
|
|
12
|
+
return join(homedir(), path.slice(1));
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
return path;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function resolvePath(path: string) {
|
|
19
|
+
const expandedPath = expandTilde(path);
|
|
20
|
+
return resolve(expandedPath);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function loadTheme(themePath: string): Promise<ThemeFunction | null> {
|
|
24
|
+
const resovedPath = resolvePath(themePath);
|
|
25
|
+
|
|
26
|
+
const module = (await import(resovedPath)) as ThemeModule;
|
|
27
|
+
|
|
28
|
+
if (typeof module.default !== `function`) {
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return module.default;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export { type ThemeModule, type ThemeFunction, loadTheme };
|