kaizenai 0.2.2 → 0.4.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 (73) hide show
  1. package/bin/kaizen +16 -5
  2. package/dist/client/app-icon-180.png +0 -0
  3. package/dist/client/app-icon-192.png +0 -0
  4. package/dist/client/app-icon-512.png +0 -0
  5. package/dist/client/app-icon-96.png +0 -0
  6. package/dist/client/apple-touch-icon.png +0 -0
  7. package/dist/client/assets/index-BR0jGwm_.css +32 -0
  8. package/dist/client/assets/index-CYqy6l6r.js +628 -0
  9. package/dist/client/favicon.png +0 -0
  10. package/dist/client/index.html +17 -10
  11. package/dist/client/manifest-dark.webmanifest +3 -3
  12. package/dist/client/manifest.webmanifest +3 -3
  13. package/dist/client/pwa-192.png +0 -0
  14. package/dist/client/pwa-512.png +0 -0
  15. package/dist/server/cli-supervisor.js +307 -0
  16. package/dist/server/cli.js +12371 -0
  17. package/package.json +7 -9
  18. package/dist/client/assets/index-Cojjg8ln.js +0 -598
  19. package/dist/client/assets/index-lSe049cB.css +0 -32
  20. package/src/server/acp-shared.ts +0 -315
  21. package/src/server/agent.ts +0 -1121
  22. package/src/server/attachments.ts +0 -133
  23. package/src/server/backgrounds.ts +0 -74
  24. package/src/server/cli-runtime.ts +0 -375
  25. package/src/server/cli-supervisor.ts +0 -97
  26. package/src/server/cli.ts +0 -68
  27. package/src/server/codex-app-server-protocol.ts +0 -453
  28. package/src/server/codex-app-server.ts +0 -1350
  29. package/src/server/cursor-acp.ts +0 -819
  30. package/src/server/discovery.ts +0 -322
  31. package/src/server/event-store.ts +0 -1369
  32. package/src/server/events.ts +0 -244
  33. package/src/server/external-open.ts +0 -272
  34. package/src/server/gemini-acp.ts +0 -844
  35. package/src/server/gemini-cli.ts +0 -525
  36. package/src/server/generate-title.ts +0 -36
  37. package/src/server/git-manager.ts +0 -79
  38. package/src/server/git-repository.ts +0 -101
  39. package/src/server/harness-types.ts +0 -20
  40. package/src/server/keybindings.ts +0 -177
  41. package/src/server/machine-name.ts +0 -22
  42. package/src/server/paths.ts +0 -112
  43. package/src/server/process-utils.ts +0 -22
  44. package/src/server/project-icon.ts +0 -352
  45. package/src/server/project-metadata.ts +0 -10
  46. package/src/server/provider-catalog.ts +0 -85
  47. package/src/server/provider-settings.ts +0 -155
  48. package/src/server/quick-response.ts +0 -153
  49. package/src/server/read-models.ts +0 -275
  50. package/src/server/recovery.ts +0 -507
  51. package/src/server/restart.ts +0 -56
  52. package/src/server/server.ts +0 -244
  53. package/src/server/terminal-manager.ts +0 -350
  54. package/src/server/theme-settings.ts +0 -179
  55. package/src/server/update-manager.ts +0 -230
  56. package/src/server/usage/base-provider-usage.ts +0 -57
  57. package/src/server/usage/claude-usage.ts +0 -558
  58. package/src/server/usage/codex-usage.ts +0 -144
  59. package/src/server/usage/cursor-browser.ts +0 -120
  60. package/src/server/usage/cursor-cookies.ts +0 -390
  61. package/src/server/usage/cursor-usage.ts +0 -490
  62. package/src/server/usage/gemini-usage.ts +0 -24
  63. package/src/server/usage/provider-usage.ts +0 -61
  64. package/src/server/usage/test-helpers.ts +0 -9
  65. package/src/server/usage/types.ts +0 -54
  66. package/src/server/usage/utils.ts +0 -325
  67. package/src/server/ws-router.ts +0 -709
  68. package/src/shared/branding.ts +0 -83
  69. package/src/shared/dev-ports.ts +0 -43
  70. package/src/shared/ports.ts +0 -2
  71. package/src/shared/protocol.ts +0 -152
  72. package/src/shared/tools.ts +0 -251
  73. package/src/shared/types.ts +0 -1028
@@ -1,101 +0,0 @@
1
- import path from "node:path"
2
- import { resolveLocalPath } from "./paths"
3
-
4
- export interface ProjectRepositoryIdentity {
5
- isGitRepo: boolean
6
- localPath: string
7
- repoKey: string
8
- repoRootPath: string | null
9
- title: string
10
- worktreePath: string
11
- }
12
-
13
- function normalizeAbsolutePath(value: string, cwd: string) {
14
- const resolved = path.isAbsolute(value) ? value : path.resolve(cwd, value)
15
- return path.normalize(resolved)
16
- }
17
-
18
- export function resolveProjectWorktreePaths(localPath: string): string[] {
19
- const normalizedLocalPath = resolveLocalPath(localPath)
20
- const result = Bun.spawnSync(
21
- ["git", "-C", normalizedLocalPath, "worktree", "list", "--porcelain"],
22
- {
23
- stdout: "pipe",
24
- stderr: "pipe",
25
- }
26
- )
27
-
28
- if (result.exitCode !== 0) {
29
- return [normalizedLocalPath]
30
- }
31
-
32
- const worktreePaths: string[] = []
33
- for (const line of new TextDecoder().decode(result.stdout).split("\n")) {
34
- const match = line.match(/^worktree\s+(.+)$/)
35
- if (!match?.[1]) continue
36
- worktreePaths.push(normalizeAbsolutePath(match[1], normalizedLocalPath))
37
- }
38
-
39
- return worktreePaths.length > 0 ? [...new Set(worktreePaths)] : [normalizedLocalPath]
40
- }
41
-
42
- export function resolveProjectRepositoryIdentity(localPath: string): ProjectRepositoryIdentity {
43
- const normalizedLocalPath = resolveLocalPath(localPath)
44
- const fallbackTitle = path.basename(normalizedLocalPath) || normalizedLocalPath
45
- const fallbackRepoKey = `path:${normalizedLocalPath}`
46
-
47
- const result = Bun.spawnSync(
48
- [
49
- "git",
50
- "-C",
51
- normalizedLocalPath,
52
- "rev-parse",
53
- "--path-format=absolute",
54
- "--show-toplevel",
55
- "--git-common-dir",
56
- ],
57
- {
58
- stdout: "pipe",
59
- stderr: "pipe",
60
- }
61
- )
62
-
63
- if (result.exitCode !== 0) {
64
- return {
65
- isGitRepo: false,
66
- localPath: normalizedLocalPath,
67
- repoKey: fallbackRepoKey,
68
- repoRootPath: null,
69
- title: fallbackTitle,
70
- worktreePath: normalizedLocalPath,
71
- }
72
- }
73
-
74
- const stdout = new TextDecoder().decode(result.stdout).trim()
75
- const [repoRootPathRaw, gitCommonDirRaw] = stdout.split("\n").map((line) => line.trim())
76
- if (!repoRootPathRaw || !gitCommonDirRaw) {
77
- return {
78
- isGitRepo: false,
79
- localPath: normalizedLocalPath,
80
- repoKey: fallbackRepoKey,
81
- repoRootPath: null,
82
- title: fallbackTitle,
83
- worktreePath: normalizedLocalPath,
84
- }
85
- }
86
-
87
- const gitCommonDir = normalizeAbsolutePath(gitCommonDirRaw, normalizedLocalPath)
88
- const repoRootPath = normalizeAbsolutePath(repoRootPathRaw, normalizedLocalPath)
89
- const repositoryDisplayPath = path.basename(gitCommonDir) === ".git"
90
- ? path.dirname(gitCommonDir)
91
- : repoRootPath
92
-
93
- return {
94
- isGitRepo: true,
95
- localPath: normalizedLocalPath,
96
- repoKey: `git:${gitCommonDir}`,
97
- repoRootPath: repositoryDisplayPath,
98
- title: path.basename(repositoryDisplayPath) || fallbackTitle,
99
- worktreePath: repoRootPath,
100
- }
101
- }
@@ -1,20 +0,0 @@
1
- import type { AccountInfo, AgentProvider, ChatUsageSnapshot, NormalizedToolCall, TranscriptEntry } from "../shared/types"
2
-
3
- export interface HarnessEvent {
4
- type: "transcript" | "session_token" | "usage"
5
- entry?: TranscriptEntry
6
- sessionToken?: string
7
- usage?: ChatUsageSnapshot
8
- }
9
-
10
- export interface HarnessToolRequest {
11
- tool: NormalizedToolCall & { toolKind: "ask_user_question" | "exit_plan_mode" }
12
- }
13
-
14
- export interface HarnessTurn {
15
- provider: AgentProvider
16
- stream: AsyncIterable<HarnessEvent>
17
- getAccountInfo?: () => Promise<AccountInfo | null>
18
- interrupt: () => Promise<void>
19
- close: () => void
20
- }
@@ -1,177 +0,0 @@
1
- import { watch, type FSWatcher } from "node:fs"
2
- import { mkdir, readFile, writeFile } from "node:fs/promises"
3
- import { homedir } from "node:os"
4
- import path from "node:path"
5
- import { getKeybindingsFilePath, LOG_PREFIX } from "../shared/branding"
6
- import { DEFAULT_KEYBINDINGS, type KeybindingAction, type KeybindingsSnapshot } from "../shared/types"
7
-
8
- const KEYBINDING_ACTIONS = Object.keys(DEFAULT_KEYBINDINGS) as KeybindingAction[]
9
-
10
- type KeybindingsFile = Partial<Record<KeybindingAction, unknown>>
11
-
12
- export class KeybindingsManager {
13
- readonly filePath: string
14
- private watcher: FSWatcher | null = null
15
- private snapshot: KeybindingsSnapshot
16
- private readonly listeners = new Set<(snapshot: KeybindingsSnapshot) => void>()
17
-
18
- constructor(filePath = getKeybindingsFilePath(homedir())) {
19
- this.filePath = filePath
20
- this.snapshot = createDefaultSnapshot(this.filePath)
21
- }
22
-
23
- async initialize() {
24
- await mkdir(path.dirname(this.filePath), { recursive: true })
25
- const file = Bun.file(this.filePath)
26
- if (!(await file.exists())) {
27
- await writeFile(this.filePath, `${JSON.stringify(DEFAULT_KEYBINDINGS, null, 2)}\n`, "utf8")
28
- }
29
- await this.reload()
30
- this.startWatching()
31
- }
32
-
33
- dispose() {
34
- this.watcher?.close()
35
- this.watcher = null
36
- this.listeners.clear()
37
- }
38
-
39
- getSnapshot() {
40
- return this.snapshot
41
- }
42
-
43
- onChange(listener: (snapshot: KeybindingsSnapshot) => void) {
44
- this.listeners.add(listener)
45
- return () => {
46
- this.listeners.delete(listener)
47
- }
48
- }
49
-
50
- async reload() {
51
- const nextSnapshot = await readKeybindingsSnapshot(this.filePath)
52
- this.setSnapshot(nextSnapshot)
53
- }
54
-
55
- async write(bindings: Partial<Record<KeybindingAction, string[]>>) {
56
- const nextSnapshot = normalizeKeybindings(bindings, this.filePath)
57
- await mkdir(path.dirname(this.filePath), { recursive: true })
58
- await writeFile(this.filePath, `${JSON.stringify(nextSnapshot.bindings, null, 2)}\n`, "utf8")
59
- this.setSnapshot(nextSnapshot)
60
- return nextSnapshot
61
- }
62
-
63
- private setSnapshot(snapshot: KeybindingsSnapshot) {
64
- this.snapshot = snapshot
65
- for (const listener of this.listeners) {
66
- listener(snapshot)
67
- }
68
- }
69
-
70
- private startWatching() {
71
- this.watcher?.close()
72
- try {
73
- this.watcher = watch(path.dirname(this.filePath), { persistent: false }, (_eventType, filename) => {
74
- if (filename && filename !== path.basename(this.filePath)) {
75
- return
76
- }
77
- void this.reload().catch((error: unknown) => {
78
- console.warn(`${LOG_PREFIX} Failed to reload keybindings:`, error)
79
- })
80
- })
81
- } catch (error) {
82
- console.warn(`${LOG_PREFIX} Failed to watch keybindings file:`, error)
83
- this.watcher = null
84
- }
85
- }
86
- }
87
-
88
- export async function readKeybindingsSnapshot(filePath: string) {
89
- try {
90
- const text = await readFile(filePath, "utf8")
91
- if (!text.trim()) {
92
- return createDefaultSnapshot(filePath, "Keybindings file was empty. Using defaults.")
93
- }
94
- const parsed = JSON.parse(text) as KeybindingsFile
95
- return normalizeKeybindings(parsed, filePath)
96
- } catch (error) {
97
- if ((error as NodeJS.ErrnoException)?.code === "ENOENT") {
98
- return createDefaultSnapshot(filePath)
99
- }
100
-
101
- if (error instanceof SyntaxError) {
102
- return createDefaultSnapshot(filePath, "Keybindings file is invalid JSON. Using defaults.")
103
- }
104
-
105
- throw error
106
- }
107
- }
108
-
109
- export function normalizeKeybindings(value: KeybindingsFile | null | undefined, filePath = getKeybindingsFilePath(homedir())): KeybindingsSnapshot {
110
- const warnings: string[] = []
111
- const source = value && typeof value === "object" && !Array.isArray(value)
112
- ? value
113
- : null
114
-
115
- if (!source) {
116
- return createDefaultSnapshot(filePath, "Keybindings file must contain a JSON object. Using defaults.")
117
- }
118
-
119
- const bindings = {} as Record<KeybindingAction, string[]>
120
- for (const action of KEYBINDING_ACTIONS) {
121
- const rawValue = source[action]
122
- if (!Array.isArray(rawValue)) {
123
- bindings[action] = [...DEFAULT_KEYBINDINGS[action]]
124
- if (rawValue !== undefined) {
125
- warnings.push(`${action} must be an array of shortcut strings`)
126
- }
127
- continue
128
- }
129
-
130
- const normalized = rawValue
131
- .filter((entry): entry is string => typeof entry === "string")
132
- .map((entry) => entry.trim())
133
- .map((entry) => entry.toLowerCase())
134
- .filter(Boolean)
135
-
136
- if (normalized.length === 0) {
137
- bindings[action] = [...DEFAULT_KEYBINDINGS[action]]
138
- if (rawValue.length > 0 || source[action] !== undefined) {
139
- warnings.push(`${action} did not contain any valid shortcut strings`)
140
- }
141
- continue
142
- }
143
-
144
- bindings[action] = normalized
145
- }
146
-
147
- return {
148
- bindings,
149
- warning: warnings.length > 0 ? `Some keybindings were reset to defaults: ${warnings.join("; ")}` : null,
150
- filePathDisplay: formatDisplayPath(filePath),
151
- }
152
- }
153
-
154
- function createDefaultSnapshot(filePath: string, warning: string | null = null): KeybindingsSnapshot {
155
- return {
156
- bindings: {
157
- submitChatMessage: [...DEFAULT_KEYBINDINGS.submitChatMessage],
158
- toggleProjectsSidebar: [...DEFAULT_KEYBINDINGS.toggleProjectsSidebar],
159
- toggleEmbeddedTerminal: [...DEFAULT_KEYBINDINGS.toggleEmbeddedTerminal],
160
- toggleRightSidebar: [...DEFAULT_KEYBINDINGS.toggleRightSidebar],
161
- openInFinder: [...DEFAULT_KEYBINDINGS.openInFinder],
162
- openInEditor: [...DEFAULT_KEYBINDINGS.openInEditor],
163
- addSplitTerminal: [...DEFAULT_KEYBINDINGS.addSplitTerminal],
164
- },
165
- warning,
166
- filePathDisplay: formatDisplayPath(filePath),
167
- }
168
- }
169
-
170
- function formatDisplayPath(filePath: string) {
171
- const homePath = homedir()
172
- if (filePath === homePath) return "~"
173
- if (filePath.startsWith(`${homePath}${path.sep}`)) {
174
- return `~${filePath.slice(homePath.length)}`
175
- }
176
- return filePath
177
- }
@@ -1,22 +0,0 @@
1
- import { hostname } from "node:os"
2
- import process from "node:process"
3
- import { spawnSync } from "node:child_process"
4
-
5
- function runAndRead(command: string, args: string[]) {
6
- const result = spawnSync(command, args, { encoding: "utf8" })
7
- if (result.status !== 0) return null
8
- const value = result.stdout.trim()
9
- return value || null
10
- }
11
-
12
- export function getMachineDisplayName() {
13
- if (process.platform === "darwin") {
14
- const computerName = runAndRead("scutil", ["--get", "ComputerName"])
15
- if (computerName) {
16
- return computerName
17
- }
18
- }
19
-
20
- const rawHostname = hostname().trim()
21
- return rawHostname.replace(/\.local$|\.lan$/i, "") || "This Machine"
22
- }
@@ -1,112 +0,0 @@
1
- import { statSync } from "node:fs"
2
- import { mkdir, readdir, stat } from "node:fs/promises"
3
- import { homedir } from "node:os"
4
- import path from "node:path"
5
- import type { DirectoryBrowserSnapshot } from "../shared/types"
6
-
7
- export function getDefaultDirectoryRoot() {
8
- const home = homedir()
9
- const candidates = [
10
- path.join(home, "Documents"),
11
- path.join(home, "projects"),
12
- path.join(home, "Projects"),
13
- home,
14
- ]
15
-
16
- for (const candidate of candidates) {
17
- try {
18
- const info = statSync(candidate)
19
- if (info.isDirectory()) {
20
- return candidate
21
- }
22
- } catch {
23
- continue
24
- }
25
- }
26
-
27
- if (process.platform === "win32") {
28
- return path.parse(home).root || "C:\\"
29
- }
30
- return "/"
31
- }
32
-
33
- export function resolveLocalPath(localPath: string) {
34
- const trimmed = localPath.trim()
35
- if (!trimmed) {
36
- throw new Error("Project path is required")
37
- }
38
- if (trimmed === "~") {
39
- return homedir()
40
- }
41
- if (trimmed.startsWith("~/")) {
42
- return path.join(homedir(), trimmed.slice(2))
43
- }
44
- return path.resolve(trimmed)
45
- }
46
-
47
- export async function ensureProjectDirectory(localPath: string) {
48
- const resolvedPath = resolveLocalPath(localPath)
49
-
50
- await mkdir(resolvedPath, { recursive: true })
51
- const info = await stat(resolvedPath)
52
- if (!info.isDirectory()) {
53
- throw new Error("Project path must be a directory")
54
- }
55
- }
56
-
57
- export async function requireProjectDirectory(localPath: string) {
58
- const resolvedPath = resolveLocalPath(localPath)
59
- const info = await stat(resolvedPath).catch(() => null)
60
- if (!info) {
61
- throw new Error(`Project folder not found: ${resolvedPath}`)
62
- }
63
- if (!info.isDirectory()) {
64
- throw new Error("Project path must be a directory")
65
- }
66
- }
67
-
68
- function getDirectoryRoots() {
69
- const home = homedir()
70
- if (process.platform === "win32") {
71
- const drive = path.parse(home).root || "C:\\"
72
- return [
73
- { name: "Home", localPath: home },
74
- { name: drive, localPath: drive },
75
- ]
76
- }
77
-
78
- return [
79
- { name: "Home", localPath: home },
80
- { name: "/", localPath: "/" },
81
- ]
82
- }
83
-
84
- export async function listProjectDirectories(localPath?: string): Promise<DirectoryBrowserSnapshot> {
85
- const currentPath = localPath ? resolveLocalPath(localPath) : getDefaultDirectoryRoot()
86
- const info = await stat(currentPath).catch(() => null)
87
-
88
- if (!info) {
89
- throw new Error(`Folder not found: ${currentPath}`)
90
- }
91
- if (!info.isDirectory()) {
92
- throw new Error("Project path must be a directory")
93
- }
94
-
95
- const directoryEntries = await readdir(currentPath, { withFileTypes: true })
96
- const entries = directoryEntries
97
- .filter((entry) => entry.isDirectory())
98
- .map((entry) => ({
99
- name: entry.name,
100
- localPath: path.join(currentPath, entry.name),
101
- }))
102
- .sort((a, b) => a.name.localeCompare(b.name))
103
-
104
- const parentPath = path.dirname(currentPath) === currentPath ? null : path.dirname(currentPath)
105
-
106
- return {
107
- currentPath,
108
- parentPath,
109
- roots: getDirectoryRoots(),
110
- entries,
111
- }
112
- }
@@ -1,22 +0,0 @@
1
- import { spawn, spawnSync } from "node:child_process"
2
-
3
- export function spawnDetached(command: string, args: string[]) {
4
- spawn(command, args, { stdio: "ignore", detached: true }).unref()
5
- }
6
-
7
- export function hasCommand(command: string) {
8
- const result = spawnSync("sh", ["-lc", `command -v ${command}`], { stdio: "ignore" })
9
- return result.status === 0
10
- }
11
-
12
- export function resolveCommandPath(command: string) {
13
- const result = spawnSync("sh", ["-lc", `command -v ${command}`], { stdio: ["ignore", "pipe", "ignore"] })
14
- if (result.status !== 0) return null
15
- const output = result.stdout?.toString().trim()
16
- return output || null
17
- }
18
-
19
- export function canOpenMacApp(appName: string) {
20
- const result = spawnSync("open", ["-Ra", appName], { stdio: "ignore" })
21
- return result.status === 0
22
- }