kajji 0.1.0 → 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.
Files changed (49) hide show
  1. package/README.md +33 -30
  2. package/bin/kajji +60 -0
  3. package/package.json +35 -55
  4. package/script/postinstall.mjs +50 -0
  5. package/bin/kajji.js +0 -2
  6. package/src/App.tsx +0 -229
  7. package/src/commander/bookmarks.ts +0 -129
  8. package/src/commander/diff.ts +0 -186
  9. package/src/commander/executor.ts +0 -285
  10. package/src/commander/files.ts +0 -87
  11. package/src/commander/log.ts +0 -99
  12. package/src/commander/operations.ts +0 -313
  13. package/src/commander/types.ts +0 -21
  14. package/src/components/AnsiText.tsx +0 -77
  15. package/src/components/BorderBox.tsx +0 -124
  16. package/src/components/FileTreeList.tsx +0 -105
  17. package/src/components/Layout.tsx +0 -48
  18. package/src/components/Panel.tsx +0 -143
  19. package/src/components/RevisionPicker.tsx +0 -165
  20. package/src/components/StatusBar.tsx +0 -158
  21. package/src/components/modals/BookmarkNameModal.tsx +0 -170
  22. package/src/components/modals/DescribeModal.tsx +0 -124
  23. package/src/components/modals/HelpModal.tsx +0 -372
  24. package/src/components/modals/RevisionPickerModal.tsx +0 -70
  25. package/src/components/modals/UndoModal.tsx +0 -75
  26. package/src/components/panels/BookmarksPanel.tsx +0 -768
  27. package/src/components/panels/CommandLogPanel.tsx +0 -40
  28. package/src/components/panels/LogPanel.tsx +0 -774
  29. package/src/components/panels/MainArea.tsx +0 -354
  30. package/src/context/command.tsx +0 -106
  31. package/src/context/commandlog.tsx +0 -45
  32. package/src/context/dialog.tsx +0 -217
  33. package/src/context/focus.tsx +0 -63
  34. package/src/context/helper.tsx +0 -24
  35. package/src/context/keybind.tsx +0 -51
  36. package/src/context/loading.tsx +0 -68
  37. package/src/context/sync.tsx +0 -868
  38. package/src/context/theme.tsx +0 -90
  39. package/src/context/types.ts +0 -51
  40. package/src/index.tsx +0 -15
  41. package/src/keybind/index.ts +0 -2
  42. package/src/keybind/parser.ts +0 -88
  43. package/src/keybind/types.ts +0 -83
  44. package/src/theme/index.ts +0 -3
  45. package/src/theme/presets/lazygit.ts +0 -45
  46. package/src/theme/presets/opencode.ts +0 -45
  47. package/src/theme/types.ts +0 -47
  48. package/src/utils/double-click.ts +0 -59
  49. package/src/utils/file-tree.ts +0 -154
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A simple terminal UI for [Jujutsu](https://github.com/martinvonz/jj), inspired by [lazygit](https://github.com/jesseduffield/lazygit). Built with [OpenTUI](https://github.com/sst/opentui) and [SolidJS](https://www.solidjs.com/).
6
6
 
7
- ![kajji screenshot](./kajji.png)
7
+ ![kajji screenshot](./assets/kajji.png)
8
8
 
9
9
  While learning jj I found myself coming back to lazygit to view diffs and traverse the changes I'd made quickly and easily, which has become increasingly important to me with the rise of coding agents. While there are existing jj TUIs, none quite scratched that lazygit itch.
10
10
 
@@ -20,35 +20,49 @@ Kajji is my attempt to bring the simplicity and polish of lazygit to jj, while a
20
20
 
21
21
  ## Features
22
22
 
23
- - **Full-color diffs** — works with difftastic, delta, or your configured diff tool
24
- - **Commit log** navigate jj's graph with vim-style keybindings
25
- - **Bookmarks panel** drill down into commits and files
26
- - **Collapsible file tree** with status colors (A/M/D)
27
- - **Operation log** view and restore from jj op history
28
- - **Git operations** fetch and push
29
- - **Undo/redo** with confirmation showing what will change
30
- - **Help palette** press `?` for all keybindings with fuzzy search
23
+ **Core jj operations:**
24
+ - [x] View commit log with graph
25
+ - [x] View diffs (difftastic, delta, etc.)
26
+ - [x] New / edit / describe / squash / abandon
27
+ - [x] Undo / redo with preview
28
+ - [x] Bookmarks (create, delete, rename, move)
29
+ - [x] Git fetch / push
30
+ - [x] Operation log with restore
31
+ - [ ] Rebase
32
+ - [ ] Split
33
+ - [ ] Conflict resolution
34
+
35
+ **TUI polish:**
36
+ - [x] Vim-style navigation (j/k, ctrl+u/d)
37
+ - [x] Mouse support (click, double-click, scroll)
38
+ - [x] Collapsible file tree with status colors
39
+ - [x] Help palette with fuzzy search (`?`)
40
+ - [ ] Multi-select for batch operations
41
+ - [ ] Search and filter
31
42
 
32
43
  ## Installation
33
44
 
34
- > **Requirements**: [Bun](https://bun.sh) and [jj](https://github.com/martinvonz/jj)
45
+ > **Requirements**: [jj](https://github.com/martinvonz/jj)
35
46
 
36
47
  ```bash
37
- # npm
38
- npm install -g kajji
48
+ # recommended (standalone binary, no dependencies)
49
+ curl -fsSL https://raw.githubusercontent.com/eliaskc/kajji/main/install.sh | bash
39
50
 
40
- # bun
51
+ # or via package manager
52
+ npm install -g kajji
41
53
  bun install -g kajji
42
-
43
- # pnpm
44
54
  pnpm add -g kajji
55
+ yarn global add kajji
45
56
 
46
- # or run directly
57
+ # or run directly without installing
58
+ npx kajji
47
59
  bunx kajji
48
60
  ```
49
61
 
50
62
  ### From source
51
63
 
64
+ > **Requirements**: [Bun](https://bun.sh)
65
+
52
66
  ```bash
53
67
  git clone https://github.com/eliaskc/kajji.git
54
68
  cd kajji
@@ -95,21 +109,10 @@ kajji
95
109
  | `c` | Create bookmark |
96
110
  | `d` | Delete bookmark |
97
111
  | `r` | Rename bookmark |
98
- | `b` | Create bookmark on commit |
99
-
100
- ## Next up
101
-
102
- - Multi-select for batch rebase and squash
103
- - Search and filter (log, bookmarks, files)
104
- - Workspaces tab (monitor agent commits across workspaces)
105
-
106
- ## Exploring
107
-
108
- - Interactive `jj split` (file/hunk selection)
109
- - Stacked PR creation and overview
110
- - Configuration (user config file, theme switching)
112
+ | `b` | Set bookmark on commit |
113
+ | `m` | Move bookmark |
111
114
 
112
- See [PROJECT](./context/PROJECT.md) for the full plan.
115
+ See [PROJECT](./context/PROJECT.md) for the full roadmap.
113
116
 
114
117
  ## Built With
115
118
 
package/bin/kajji ADDED
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env node
2
+
3
+ const childProcess = require("child_process")
4
+ const fs = require("fs")
5
+ const path = require("path")
6
+ const os = require("os")
7
+
8
+ function run(target) {
9
+ const result = childProcess.spawnSync(target, process.argv.slice(2), {
10
+ stdio: "inherit",
11
+ })
12
+ if (result.error) {
13
+ console.error(result.error.message)
14
+ process.exit(1)
15
+ }
16
+ process.exit(typeof result.status === "number" ? result.status : 0)
17
+ }
18
+
19
+ const envPath = process.env.KAJJI_BIN_PATH
20
+ if (envPath) {
21
+ run(envPath)
22
+ }
23
+
24
+ const scriptPath = fs.realpathSync(__filename)
25
+ const scriptDir = path.dirname(scriptPath)
26
+
27
+ const platformMap = { darwin: "darwin", linux: "linux" }
28
+ const archMap = { x64: "x64", arm64: "arm64" }
29
+
30
+ const platform = platformMap[os.platform()] || os.platform()
31
+ const arch = archMap[os.arch()] || os.arch()
32
+ const base = "kajji-" + platform + "-" + arch
33
+
34
+ function findBinary(startDir) {
35
+ let current = startDir
36
+ for (;;) {
37
+ const modules = path.join(current, "node_modules")
38
+ if (fs.existsSync(modules)) {
39
+ for (const entry of fs.readdirSync(modules)) {
40
+ if (!entry.startsWith(base)) continue
41
+ const candidate = path.join(modules, entry, "bin", "kajji")
42
+ if (fs.existsSync(candidate)) return candidate
43
+ }
44
+ }
45
+ const parent = path.dirname(current)
46
+ if (parent === current) return
47
+ current = parent
48
+ }
49
+ }
50
+
51
+ const resolved = findBinary(scriptDir)
52
+ if (!resolved) {
53
+ console.error(
54
+ `Could not find kajji binary for your platform (${platform}-${arch}).\n` +
55
+ `Try installing the platform package: npm install ${base}`
56
+ )
57
+ process.exit(1)
58
+ }
59
+
60
+ run(resolved)
package/package.json CHANGED
@@ -1,56 +1,36 @@
1
1
  {
2
- "name": "kajji",
3
- "version": "0.1.0",
4
- "description": "A terminal UI for Jujutsu: the rudder for your jj",
5
- "type": "module",
6
- "engines": {
7
- "bun": ">=1.0.0"
8
- },
9
- "bin": {
10
- "kajji": "./bin/kajji.js"
11
- },
12
- "files": ["bin", "src"],
13
- "scripts": {
14
- "dev": "NODE_ENV=development bun --watch run src/index.tsx",
15
- "test": "bun test",
16
- "check": "tsc --noEmit",
17
- "lint": "biome check .",
18
- "lint:fix": "biome check --write ."
19
- },
20
- "devDependencies": {
21
- "@biomejs/biome": "^1.9.4",
22
- "@types/bun": "latest"
23
- },
24
- "peerDependencies": {
25
- "typescript": "^5"
26
- },
27
- "dependencies": {
28
- "@babel/core": "^7.28.5",
29
- "@babel/preset-typescript": "^7.28.5",
30
- "@opentui/core": "^0.1.66",
31
- "@opentui/solid": "^0.1.66",
32
- "babel-preset-solid": "1.9.9",
33
- "fuzzysort": "^3.1.0",
34
- "ghostty-opentui": "^1.3.11",
35
- "solid-js": "1.9.9"
36
- },
37
- "keywords": [
38
- "jj",
39
- "jujutsu",
40
- "tui",
41
- "terminal",
42
- "cli",
43
- "vcs",
44
- "version-control"
45
- ],
46
- "repository": {
47
- "type": "git",
48
- "url": "git+https://github.com/eliaskc/kajji.git"
49
- },
50
- "homepage": "https://github.com/eliaskc/kajji#readme",
51
- "bugs": {
52
- "url": "https://github.com/eliaskc/kajji/issues"
53
- },
54
- "author": "eliaskc",
55
- "license": "MIT"
56
- }
2
+ "name": "kajji",
3
+ "version": "0.2.0",
4
+ "description": "A terminal UI for Jujutsu: the rudder for your jj",
5
+ "bin": {
6
+ "kajji": "./bin/kajji"
7
+ },
8
+ "scripts": {
9
+ "postinstall": "node ./script/postinstall.mjs"
10
+ },
11
+ "optionalDependencies": {
12
+ "kajji-darwin-arm64": "0.2.0",
13
+ "kajji-darwin-x64": "0.2.0",
14
+ "kajji-linux-x64": "0.2.0",
15
+ "kajji-linux-arm64": "0.2.0"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/eliaskc/kajji.git"
20
+ },
21
+ "homepage": "https://github.com/eliaskc/kajji#readme",
22
+ "bugs": {
23
+ "url": "https://github.com/eliaskc/kajji/issues"
24
+ },
25
+ "keywords": [
26
+ "jj",
27
+ "jujutsu",
28
+ "tui",
29
+ "terminal",
30
+ "cli",
31
+ "vcs",
32
+ "version-control"
33
+ ],
34
+ "author": "eliaskc",
35
+ "license": "MIT"
36
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs"
4
+ import os from "node:os"
5
+ import path from "node:path"
6
+ import { fileURLToPath } from "node:url"
7
+
8
+ const __dirname = path.dirname(fileURLToPath(import.meta.url))
9
+
10
+ const platformMap = { darwin: "darwin", linux: "linux" }
11
+ const archMap = { x64: "x64", arm64: "arm64" }
12
+
13
+ const platform = platformMap[os.platform()]
14
+ const arch = archMap[os.arch()]
15
+
16
+ if (!platform || !arch) {
17
+ console.log(`Unsupported platform: ${os.platform()}-${os.arch()}`)
18
+ process.exit(0)
19
+ }
20
+
21
+ const packageName = `kajji-${platform}-${arch}`
22
+
23
+ let packageDir
24
+ try {
25
+ const packageJsonPath = require.resolve(`${packageName}/package.json`, {
26
+ paths: [path.join(__dirname, "..")],
27
+ })
28
+ packageDir = path.dirname(packageJsonPath)
29
+ } catch {
30
+ console.log(`Platform package ${packageName} not found, skipping symlink`)
31
+ process.exit(0)
32
+ }
33
+
34
+ const binaryPath = path.join(packageDir, "bin", "kajji")
35
+ const targetPath = path.join(__dirname, "..", "bin", "kajji-binary")
36
+
37
+ if (!fs.existsSync(binaryPath)) {
38
+ console.log(`Binary not found at ${binaryPath}`)
39
+ process.exit(0)
40
+ }
41
+
42
+ try {
43
+ if (fs.existsSync(targetPath)) {
44
+ fs.unlinkSync(targetPath)
45
+ }
46
+ fs.symlinkSync(binaryPath, targetPath)
47
+ console.log(`kajji binary linked: ${targetPath} -> ${binaryPath}`)
48
+ } catch (err) {
49
+ console.log(`Could not create symlink: ${err.message}`)
50
+ }
package/bin/kajji.js DELETED
@@ -1,2 +0,0 @@
1
- #!/usr/bin/env bun
2
- import "../src/index.tsx"
package/src/App.tsx DELETED
@@ -1,229 +0,0 @@
1
- import { useRenderer } from "@opentui/solid"
2
- import { onMount } from "solid-js"
3
- import { jjGitFetch, jjGitPush } from "./commander/operations"
4
- import { Layout } from "./components/Layout"
5
- import { HelpModal } from "./components/modals/HelpModal"
6
- import { BookmarksPanel } from "./components/panels/BookmarksPanel"
7
- import { LogPanel } from "./components/panels/LogPanel"
8
- import { MainArea } from "./components/panels/MainArea"
9
- import { CommandProvider, useCommand } from "./context/command"
10
- import { CommandLogProvider, useCommandLog } from "./context/commandlog"
11
- import { DialogContainer, DialogProvider, useDialog } from "./context/dialog"
12
- import { FocusProvider, useFocus } from "./context/focus"
13
- import { KeybindProvider } from "./context/keybind"
14
- import { LoadingProvider, useLoading } from "./context/loading"
15
- import { SyncProvider, useSync } from "./context/sync"
16
- import { ThemeProvider } from "./context/theme"
17
-
18
- function AppContent() {
19
- const renderer = useRenderer()
20
- const { loadLog, loadBookmarks, refresh } = useSync()
21
- const focus = useFocus()
22
- const command = useCommand()
23
- const dialog = useDialog()
24
- const commandLog = useCommandLog()
25
- const globalLoading = useLoading()
26
-
27
- onMount(() => {
28
- loadLog()
29
- loadBookmarks()
30
-
31
- renderer.console.keyBindings = [
32
- { name: "y", ctrl: true, action: "copy-selection" },
33
- ]
34
- renderer.console.onCopySelection = (text) => {
35
- const proc = Bun.spawn(["pbcopy"], { stdin: "pipe" })
36
- proc.stdin.write(text)
37
- proc.stdin.end()
38
- }
39
- })
40
-
41
- command.register(() => [
42
- {
43
- id: "global.quit",
44
- title: "quit",
45
- keybind: "quit",
46
- context: "global",
47
- type: "action",
48
- onSelect: () => {
49
- renderer.destroy()
50
- process.exit(0)
51
- },
52
- },
53
- ...(Bun.env.NODE_ENV === "development"
54
- ? [
55
- {
56
- id: "global.toggle_console",
57
- title: "toggle console",
58
- keybind: "toggle_console" as const,
59
- context: "global" as const,
60
- type: "action" as const,
61
- onSelect: () => renderer.console.toggle(),
62
- },
63
- ]
64
- : []),
65
- {
66
- id: "global.focus_next",
67
- title: "focus next panel",
68
- keybind: "focus_next",
69
- context: "global",
70
- type: "navigation",
71
- visibility: "help-only",
72
- onSelect: () => focus.cycleNext(),
73
- },
74
- {
75
- id: "global.focus_prev",
76
- title: "focus previous panel",
77
- keybind: "focus_prev",
78
- context: "global",
79
- type: "navigation",
80
- visibility: "help-only",
81
- onSelect: () => focus.cyclePrev(),
82
- },
83
- {
84
- id: "global.focus_panel_1",
85
- title: "focus log panel",
86
- keybind: "focus_panel_1",
87
- context: "global",
88
- type: "navigation",
89
- visibility: "help-only",
90
- onSelect: () => focus.setPanel("log"),
91
- },
92
- {
93
- id: "global.focus_panel_2",
94
- title: "focus refs panel",
95
- keybind: "focus_panel_2",
96
- context: "global",
97
- type: "navigation",
98
- visibility: "help-only",
99
- onSelect: () => focus.setPanel("refs"),
100
- },
101
- {
102
- id: "global.focus_panel_3",
103
- title: "focus detail panel",
104
- keybind: "focus_panel_3",
105
- context: "global",
106
- type: "navigation",
107
- visibility: "help-only",
108
- onSelect: () => focus.setPanel("detail"),
109
- },
110
- {
111
- id: "global.help",
112
- title: "help",
113
- keybind: "help",
114
- context: "global",
115
- type: "action",
116
- onSelect: () =>
117
- dialog.toggle("help", () => <HelpModal />, {
118
- hints: [{ key: "enter", label: "execute" }],
119
- }),
120
- },
121
- {
122
- id: "global.refresh",
123
- title: "refresh",
124
- keybind: "refresh",
125
- context: "global",
126
- type: "action",
127
- visibility: "help-only",
128
- onSelect: () => refresh(),
129
- },
130
- {
131
- id: "global.git_fetch",
132
- title: "git fetch",
133
- keybind: "jj_git_fetch",
134
- context: "global",
135
- type: "git",
136
- visibility: "help-only",
137
- onSelect: async () => {
138
- const result = await globalLoading.run("Fetching...", () =>
139
- jjGitFetch(),
140
- )
141
- commandLog.addEntry(result)
142
- if (result.success) {
143
- refresh()
144
- }
145
- },
146
- },
147
- {
148
- id: "global.git_fetch_all",
149
- title: "git fetch all",
150
- keybind: "jj_git_fetch_all",
151
- context: "global",
152
- type: "git",
153
- visibility: "help-only",
154
- onSelect: async () => {
155
- const result = await globalLoading.run("Fetching all...", () =>
156
- jjGitFetch({ allRemotes: true }),
157
- )
158
- commandLog.addEntry(result)
159
- if (result.success) {
160
- refresh()
161
- }
162
- },
163
- },
164
- {
165
- id: "global.git_push",
166
- title: "git push",
167
- keybind: "jj_git_push",
168
- context: "global",
169
- type: "git",
170
- visibility: "help-only",
171
- onSelect: async () => {
172
- const result = await globalLoading.run("Pushing...", () => jjGitPush())
173
- commandLog.addEntry(result)
174
- if (result.success) {
175
- refresh()
176
- }
177
- },
178
- },
179
- {
180
- id: "global.git_push_all",
181
- title: "git push all",
182
- keybind: "jj_git_push_all",
183
- context: "global",
184
- type: "git",
185
- visibility: "help-only",
186
- onSelect: async () => {
187
- const result = await globalLoading.run("Pushing all...", () =>
188
- jjGitPush({ all: true }),
189
- )
190
- commandLog.addEntry(result)
191
- if (result.success) {
192
- refresh()
193
- }
194
- },
195
- },
196
- ])
197
-
198
- return (
199
- <DialogContainer>
200
- <Layout
201
- top={<LogPanel />}
202
- bottom={<BookmarksPanel />}
203
- right={<MainArea />}
204
- />
205
- </DialogContainer>
206
- )
207
- }
208
-
209
- export function App() {
210
- return (
211
- <ThemeProvider>
212
- <FocusProvider>
213
- <LoadingProvider>
214
- <SyncProvider>
215
- <KeybindProvider>
216
- <CommandLogProvider>
217
- <DialogProvider>
218
- <CommandProvider>
219
- <AppContent />
220
- </CommandProvider>
221
- </DialogProvider>
222
- </CommandLogProvider>
223
- </KeybindProvider>
224
- </SyncProvider>
225
- </LoadingProvider>
226
- </FocusProvider>
227
- </ThemeProvider>
228
- )
229
- }
@@ -1,129 +0,0 @@
1
- import { execute } from "./executor"
2
- import type { OperationResult } from "./operations"
3
-
4
- export interface Bookmark {
5
- name: string
6
- changeId: string
7
- commitId: string
8
- description: string
9
- isLocal: boolean
10
- remote?: string
11
- }
12
-
13
- export interface FetchBookmarksOptions {
14
- cwd?: string
15
- allRemotes?: boolean
16
- }
17
-
18
- export async function fetchBookmarks(
19
- options: FetchBookmarksOptions = {},
20
- ): Promise<Bookmark[]> {
21
- const args = ["bookmark", "list"]
22
-
23
- if (options.allRemotes) {
24
- args.push("--all-remotes")
25
- }
26
-
27
- const result = await execute(args, { cwd: options.cwd })
28
-
29
- if (!result.success) {
30
- throw new Error(`jj bookmark list failed: ${result.stderr}`)
31
- }
32
-
33
- return parseBookmarkOutput(result.stdout)
34
- }
35
-
36
- export function parseBookmarkOutput(output: string): Bookmark[] {
37
- const bookmarks: Bookmark[] = []
38
- const lines = output.split("\n")
39
-
40
- for (const line of lines) {
41
- if (!line.trim()) continue
42
-
43
- const isRemote = line.startsWith(" @")
44
-
45
- if (isRemote) {
46
- const match = line.match(/^\s+@(\S+):\s+(\S+)\s+(\S+)\s*(.*)$/)
47
- if (match) {
48
- bookmarks.push({
49
- name: match[1] ?? "",
50
- changeId: match[2] ?? "",
51
- commitId: match[3] ?? "",
52
- description: match[4]?.trim() ?? "",
53
- isLocal: false,
54
- remote: match[1],
55
- })
56
- }
57
- } else {
58
- const match = line.match(/^(\S+):\s+(\S+)\s+(\S+)\s*(.*)$/)
59
- if (match) {
60
- bookmarks.push({
61
- name: match[1] ?? "",
62
- changeId: match[2] ?? "",
63
- commitId: match[3] ?? "",
64
- description: match[4]?.trim() ?? "",
65
- isLocal: true,
66
- })
67
- }
68
- }
69
- }
70
-
71
- return bookmarks
72
- }
73
-
74
- export async function jjBookmarkCreate(
75
- name: string,
76
- options?: { revision?: string },
77
- ): Promise<OperationResult> {
78
- const args = ["bookmark", "create", name]
79
- if (options?.revision) {
80
- args.push("-r", options.revision)
81
- }
82
- const result = await execute(args)
83
- return {
84
- ...result,
85
- command: `jj ${args.join(" ")}`,
86
- }
87
- }
88
-
89
- export async function jjBookmarkDelete(name: string): Promise<OperationResult> {
90
- const args = ["bookmark", "delete", name]
91
- const result = await execute(args)
92
- return {
93
- ...result,
94
- command: `jj ${args.join(" ")}`,
95
- }
96
- }
97
-
98
- export async function jjBookmarkRename(
99
- oldName: string,
100
- newName: string,
101
- ): Promise<OperationResult> {
102
- const args = ["bookmark", "rename", oldName, newName]
103
- const result = await execute(args)
104
- return {
105
- ...result,
106
- command: `jj ${args.join(" ")}`,
107
- }
108
- }
109
-
110
- export async function jjBookmarkForget(name: string): Promise<OperationResult> {
111
- const args = ["bookmark", "forget", name]
112
- const result = await execute(args)
113
- return {
114
- ...result,
115
- command: `jj ${args.join(" ")}`,
116
- }
117
- }
118
-
119
- export async function jjBookmarkSet(
120
- name: string,
121
- revision: string,
122
- ): Promise<OperationResult> {
123
- const args = ["bookmark", "set", name, "-r", revision]
124
- const result = await execute(args)
125
- return {
126
- ...result,
127
- command: `jj ${args.join(" ")}`,
128
- }
129
- }