stage-tui 1.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -0
- package/bin/stage +51 -0
- package/index.ts +22 -0
- package/package.json +46 -0
- package/src/ai-commit.ts +706 -0
- package/src/app.tsx +127 -0
- package/src/config-file.ts +40 -0
- package/src/config.ts +283 -0
- package/src/git-branch-name.ts +13 -0
- package/src/git-process.ts +25 -0
- package/src/git-status-parser.ts +103 -0
- package/src/git.ts +298 -0
- package/src/hooks/use-branch-dialog-controller.ts +188 -0
- package/src/hooks/use-commit-history-controller.ts +130 -0
- package/src/hooks/use-git-tui-controller.ts +310 -0
- package/src/hooks/use-git-tui-effects.ts +168 -0
- package/src/hooks/use-git-tui-keyboard.ts +293 -0
- package/src/ui/components/branch-dialog.tsx +107 -0
- package/src/ui/components/commit-dialog.tsx +68 -0
- package/src/ui/components/commit-history-dialog.tsx +87 -0
- package/src/ui/components/diff-workspace.tsx +108 -0
- package/src/ui/components/footer-bar.tsx +65 -0
- package/src/ui/components/shortcuts-dialog.tsx +53 -0
- package/src/ui/components/top-bar.tsx +36 -0
- package/src/ui/diff-style.ts +33 -0
- package/src/ui/theme.ts +151 -0
- package/src/ui/types.ts +21 -0
- package/src/ui/utils.ts +99 -0
package/README.md
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Stage [Beta]
|
|
2
|
+
|
|
3
|
+
A calm, opinionated Git TUI.
|
|
4
|
+
|
|
5
|
+
Inspired by GitHub Desktop. Built with OpenTUI.
|
|
6
|
+
|
|
7
|
+
Stage keeps the core Git loop focused: review changes, stage with intent, commit clearly, and move on.
|
|
8
|
+
|
|
9
|
+
What it does:
|
|
10
|
+
|
|
11
|
+
- Diff-first workflow with per-file stage/unstage
|
|
12
|
+
- Fast branch switching and commit history view
|
|
13
|
+
- Optional AI-generated conventional commits
|
|
14
|
+
|
|
15
|
+
<video src="https://github.com/user-attachments/assets/5f61e323-bb5e-4b11-9352-182d1a884feb" controls></video>
|
|
16
|
+
|
|
17
|
+
## Get Started
|
|
18
|
+
|
|
19
|
+
Prerequisite: [Bun](https://bun.sh)
|
|
20
|
+
|
|
21
|
+
Install:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bun add -g stage-tui
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Run:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
stage
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Debug with your local checkout from any repo:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
export STAGE_DEV_PATH=/absolute/path/to/stage-tui
|
|
37
|
+
stage --dev
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Use
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
stage # use installed npm package
|
|
44
|
+
stage --dev # use local checkout at STAGE_DEV_PATH
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Configuration
|
|
48
|
+
|
|
49
|
+
Config file path:
|
|
50
|
+
|
|
51
|
+
- `${XDG_CONFIG_HOME:-~/.config}/stage-manager/config.toml`
|
|
52
|
+
|
|
53
|
+
Stage creates this file on first launch.
|
|
54
|
+
|
|
55
|
+
Optional overrides:
|
|
56
|
+
|
|
57
|
+
- `STAGE_CONFIG=/path/to/config.toml`
|
|
58
|
+
- `./.stage-manager.toml` (repo-local)
|
|
59
|
+
|
|
60
|
+
Theme behavior:
|
|
61
|
+
|
|
62
|
+
- `ui.theme = "auto"` follows OS appearance (default)
|
|
63
|
+
- `ui.theme = "dark"` or `ui.theme = "light"` forces a mode
|
|
64
|
+
|
|
65
|
+
<details>
|
|
66
|
+
<summary>All Config Options (Defaults)</summary>
|
|
67
|
+
|
|
68
|
+
```toml
|
|
69
|
+
[ui]
|
|
70
|
+
diff_view = "unified" # "unified" | "split"
|
|
71
|
+
theme = "auto" # "auto" | "dark" | "light"
|
|
72
|
+
hide_whitespace_changes = true
|
|
73
|
+
show_shortcuts_hint = true
|
|
74
|
+
|
|
75
|
+
[history]
|
|
76
|
+
limit = 200
|
|
77
|
+
|
|
78
|
+
[git]
|
|
79
|
+
auto_stage_on_commit = true
|
|
80
|
+
|
|
81
|
+
[ai]
|
|
82
|
+
enabled = false
|
|
83
|
+
provider = "cerebras" # currently only supported provider
|
|
84
|
+
api_key = "" # required when enabled = true
|
|
85
|
+
model = "gpt-oss-120b"
|
|
86
|
+
reasoning_effort = "low" # "low" | "medium" | "high"
|
|
87
|
+
max_files = 32
|
|
88
|
+
max_chars_per_file = 4000
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
</details>
|
|
92
|
+
|
|
93
|
+
<details>
|
|
94
|
+
<summary>Shortcuts</summary>
|
|
95
|
+
|
|
96
|
+
- `?`: toggle shortcuts overlay
|
|
97
|
+
- `b`: change branch
|
|
98
|
+
- `h`: open commit history
|
|
99
|
+
- `c`: open commit dialog (or AI auto-commit when enabled)
|
|
100
|
+
- `space`: include/exclude selected file for commit
|
|
101
|
+
- `↑ / ↓`: move file selection
|
|
102
|
+
- `r`: refresh
|
|
103
|
+
- `f`: fetch
|
|
104
|
+
- `l`: pull
|
|
105
|
+
- `p`: push
|
|
106
|
+
- `esc`: close dialog (or exit from main view)
|
|
107
|
+
|
|
108
|
+
</details>
|
|
109
|
+
|
|
110
|
+
## Credits
|
|
111
|
+
|
|
112
|
+
- Built with [OpenTUI](https://github.com/anomalyco/opentui)
|
package/bin/stage
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { existsSync } from "node:fs"
|
|
3
|
+
import { join, resolve } from "node:path"
|
|
4
|
+
|
|
5
|
+
function resolveDevEntry(): string {
|
|
6
|
+
const envPath = process.env.STAGE_DEV_PATH
|
|
7
|
+
if (envPath) {
|
|
8
|
+
const entry = join(resolve(envPath), "index.ts")
|
|
9
|
+
if (existsSync(entry)) {
|
|
10
|
+
return entry
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
throw new Error(`STAGE_DEV_PATH does not contain index.ts: ${entry}`)
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const cwdEntry = join(process.cwd(), "index.ts")
|
|
17
|
+
if (existsSync(cwdEntry)) {
|
|
18
|
+
return cwdEntry
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
throw new Error(
|
|
22
|
+
"stage --dev requires either STAGE_DEV_PATH=/absolute/path/to/stage-tui or running from the stage-tui repo root.",
|
|
23
|
+
)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function runDevMode(args: string[]) {
|
|
27
|
+
const entry = resolveDevEntry()
|
|
28
|
+
const child = Bun.spawn(["bun", "run", entry, ...args], {
|
|
29
|
+
stdin: "inherit",
|
|
30
|
+
stdout: "inherit",
|
|
31
|
+
stderr: "inherit",
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
process.exit(await child.exited)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const args = process.argv.slice(2)
|
|
39
|
+
const devModeIndex = args.indexOf("--dev")
|
|
40
|
+
|
|
41
|
+
if (devModeIndex >= 0) {
|
|
42
|
+
args.splice(devModeIndex, 1)
|
|
43
|
+
await runDevMode(args)
|
|
44
|
+
} else {
|
|
45
|
+
await import("../index.ts")
|
|
46
|
+
}
|
|
47
|
+
} catch (error) {
|
|
48
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
49
|
+
console.error(message)
|
|
50
|
+
process.exit(1)
|
|
51
|
+
}
|
package/index.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import { createCliRenderer } from "@opentui/core"
|
|
3
|
+
import { createRoot } from "@opentui/react"
|
|
4
|
+
|
|
5
|
+
import { App } from "./src/app"
|
|
6
|
+
import { loadStageConfig } from "./src/config"
|
|
7
|
+
|
|
8
|
+
const resolvedConfig = await loadStageConfig(process.cwd()).catch((error) => {
|
|
9
|
+
const message = error instanceof Error ? error.message : String(error)
|
|
10
|
+
console.error(`Failed to load stage config: ${message}`)
|
|
11
|
+
process.exit(1)
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
const renderer = await createCliRenderer({
|
|
15
|
+
targetFps: 30,
|
|
16
|
+
useMouse: true,
|
|
17
|
+
useConsole: false,
|
|
18
|
+
exitOnCtrlC: true,
|
|
19
|
+
onDestroy: () => process.exit(0),
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
createRoot(renderer).render(React.createElement(App, { config: resolvedConfig.config }))
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "stage-tui",
|
|
3
|
+
"version": "1.0.7",
|
|
4
|
+
"description": "Minimalist TUI Git client",
|
|
5
|
+
"author": "jenslys",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "https://github.com/jenslys/stage.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/jenslys/stage",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/jenslys/stage/issues"
|
|
13
|
+
},
|
|
14
|
+
"module": "index.ts",
|
|
15
|
+
"type": "module",
|
|
16
|
+
"private": false,
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"bin",
|
|
22
|
+
"src",
|
|
23
|
+
"index.ts",
|
|
24
|
+
"README.md"
|
|
25
|
+
],
|
|
26
|
+
"bin": {
|
|
27
|
+
"stage": "./bin/stage"
|
|
28
|
+
},
|
|
29
|
+
"scripts": {
|
|
30
|
+
"stage": "bun run index.ts"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/bun": "latest",
|
|
34
|
+
"@types/react": "^19.2.14"
|
|
35
|
+
},
|
|
36
|
+
"peerDependencies": {
|
|
37
|
+
"typescript": "^5"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@ai-sdk/cerebras": "^2.0.34",
|
|
41
|
+
"@opentui/core": "^0.1.81",
|
|
42
|
+
"@opentui/react": "^0.1.81",
|
|
43
|
+
"ai": "^6.0.97",
|
|
44
|
+
"react": "^19.2.4"
|
|
45
|
+
}
|
|
46
|
+
}
|