@kkelly-offical/kkcode 0.1.7 → 0.2.1

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 (163) hide show
  1. package/LICENSE +674 -674
  2. package/README.md +452 -387
  3. package/package.json +50 -46
  4. package/src/agent/agent.mjs +228 -220
  5. package/src/agent/custom-agent-loader.mjs +6 -3
  6. package/src/agent/generator.mjs +2 -2
  7. package/src/agent/prompt/assistant.txt +12 -0
  8. package/src/agent/prompt/bug-hunter.txt +89 -89
  9. package/src/agent/prompt/frontend-designer.txt +58 -58
  10. package/src/agent/prompt/guide.txt +1 -1
  11. package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
  12. package/src/agent/prompt/longagent-coding-agent.txt +37 -37
  13. package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
  14. package/src/agent/prompt/longagent-preview-agent.txt +63 -63
  15. package/src/command/custom-commands.mjs +2 -2
  16. package/src/commands/agent.mjs +1 -1
  17. package/src/commands/background.mjs +145 -4
  18. package/src/commands/chat.mjs +117 -76
  19. package/src/commands/config.mjs +148 -1
  20. package/src/commands/doctor.mjs +30 -6
  21. package/src/commands/init.mjs +32 -6
  22. package/src/commands/longagent.mjs +117 -0
  23. package/src/commands/mcp.mjs +275 -43
  24. package/src/commands/permission.mjs +1 -1
  25. package/src/commands/session.mjs +195 -140
  26. package/src/commands/skill.mjs +63 -0
  27. package/src/commands/theme.mjs +1 -1
  28. package/src/config/defaults.mjs +280 -260
  29. package/src/config/import-config.mjs +1 -1
  30. package/src/config/load-config.mjs +61 -4
  31. package/src/config/schema.mjs +591 -574
  32. package/src/context.mjs +4 -1
  33. package/src/core/constants.mjs +97 -91
  34. package/src/core/types.mjs +1 -1
  35. package/src/github/api.mjs +78 -78
  36. package/src/github/auth.mjs +294 -286
  37. package/src/github/flow.mjs +298 -298
  38. package/src/github/workspace.mjs +225 -212
  39. package/src/index.mjs +84 -82
  40. package/src/knowledge/frontend-aesthetics.txt +38 -38
  41. package/src/mcp/client-http.mjs +139 -141
  42. package/src/mcp/client-sse.mjs +297 -288
  43. package/src/mcp/client-stdio.mjs +534 -533
  44. package/src/mcp/constants.mjs +2 -2
  45. package/src/mcp/registry.mjs +498 -479
  46. package/src/mcp/stdio-framing.mjs +135 -133
  47. package/src/mcp/tool-result.mjs +24 -24
  48. package/src/observability/edit-diagnostics.mjs +449 -0
  49. package/src/observability/index.mjs +42 -42
  50. package/src/observability/metrics.mjs +165 -137
  51. package/src/observability/tracer.mjs +137 -137
  52. package/src/onboarding.mjs +209 -0
  53. package/src/orchestration/background-manager.mjs +567 -372
  54. package/src/orchestration/background-worker.mjs +419 -305
  55. package/src/orchestration/interruption-reason.mjs +21 -0
  56. package/src/orchestration/longagent-manager.mjs +197 -171
  57. package/src/orchestration/stage-scheduler.mjs +733 -728
  58. package/src/orchestration/subagent-router.mjs +7 -1
  59. package/src/orchestration/task-scheduler.mjs +219 -7
  60. package/src/permission/engine.mjs +1 -1
  61. package/src/permission/exec-policy.mjs +370 -370
  62. package/src/permission/file-edit-policy.mjs +108 -0
  63. package/src/permission/prompt.mjs +1 -1
  64. package/src/permission/rules.mjs +116 -7
  65. package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
  66. package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
  67. package/src/plugin/hook-bus.mjs +19 -5
  68. package/src/plugin/manifest-loader.mjs +222 -0
  69. package/src/provider/anthropic.mjs +396 -390
  70. package/src/provider/ollama.mjs +7 -1
  71. package/src/provider/openai.mjs +382 -340
  72. package/src/provider/retry-policy.mjs +74 -68
  73. package/src/provider/router.mjs +242 -241
  74. package/src/provider/sse.mjs +104 -104
  75. package/src/provider/wizard.mjs +556 -0
  76. package/src/repl/capability-facade.mjs +30 -0
  77. package/src/repl/command-surface.mjs +23 -0
  78. package/src/repl/controller-entry.mjs +40 -0
  79. package/src/repl/core-shell.mjs +208 -0
  80. package/src/repl/dialog-router.mjs +87 -0
  81. package/src/repl/input-engine.mjs +76 -0
  82. package/src/repl/keymap.mjs +7 -0
  83. package/src/repl/operator-surface.mjs +15 -0
  84. package/src/repl/permission-flow.mjs +49 -0
  85. package/src/repl/runtime-facade.mjs +36 -0
  86. package/src/repl/slash-router.mjs +62 -0
  87. package/src/repl/state-store.mjs +29 -0
  88. package/src/repl/turn-controller.mjs +58 -0
  89. package/src/repl/verification.mjs +23 -0
  90. package/src/repl.mjs +3368 -2981
  91. package/src/rules/load-rules.mjs +3 -3
  92. package/src/runtime.mjs +1 -1
  93. package/src/session/agent-transaction.mjs +86 -0
  94. package/src/session/checkpoint.mjs +302 -302
  95. package/src/session/compaction.mjs +298 -298
  96. package/src/session/engine.mjs +417 -232
  97. package/src/session/longagent-4stage.mjs +467 -460
  98. package/src/session/longagent-hybrid.mjs +1344 -1097
  99. package/src/session/longagent-plan.mjs +376 -365
  100. package/src/session/longagent-project-memory.mjs +53 -53
  101. package/src/session/longagent-scaffold.mjs +291 -291
  102. package/src/session/longagent-task-bus.mjs +138 -54
  103. package/src/session/longagent-utils.mjs +828 -472
  104. package/src/session/longagent.mjs +911 -900
  105. package/src/session/loop.mjs +1005 -930
  106. package/src/session/prompt/agent.txt +25 -25
  107. package/src/session/prompt/anthropic.txt +150 -150
  108. package/src/session/prompt/beast.txt +1 -1
  109. package/src/session/prompt/plan.txt +31 -31
  110. package/src/session/prompt/qwen.txt +46 -46
  111. package/src/session/recovery.mjs +21 -0
  112. package/src/session/rollback.mjs +196 -195
  113. package/src/session/routing-observability.mjs +72 -0
  114. package/src/session/runtime-state.mjs +47 -0
  115. package/src/session/store.mjs +523 -519
  116. package/src/session/system-prompt.mjs +308 -273
  117. package/src/session/task-validator.mjs +267 -267
  118. package/src/session/usability-gates.mjs +2 -2
  119. package/src/skill/builtin/commit.mjs +64 -64
  120. package/src/skill/builtin/design.mjs +76 -76
  121. package/src/skill/generator.mjs +18 -2
  122. package/src/skill/registry.mjs +642 -390
  123. package/src/storage/audit-store.mjs +18 -11
  124. package/src/storage/event-log.mjs +7 -1
  125. package/src/storage/ghost-commit-store.mjs +243 -245
  126. package/src/storage/paths.mjs +13 -0
  127. package/src/theme/default-theme.mjs +1 -1
  128. package/src/theme/markdown.mjs +4 -0
  129. package/src/theme/schema.mjs +1 -1
  130. package/src/theme/status-bar.mjs +162 -158
  131. package/src/tool/audit-wrapper.mjs +18 -2
  132. package/src/tool/edit-transaction.mjs +23 -0
  133. package/src/tool/executor.mjs +26 -1
  134. package/src/tool/file-read-state.mjs +65 -0
  135. package/src/tool/git-auto.mjs +526 -526
  136. package/src/tool/git-full-auto.mjs +487 -478
  137. package/src/tool/mutation-guard.mjs +54 -0
  138. package/src/tool/prompt/edit.txt +3 -3
  139. package/src/tool/prompt/multiedit.txt +1 -0
  140. package/src/tool/prompt/notebookedit.txt +2 -1
  141. package/src/tool/prompt/patch.txt +25 -24
  142. package/src/tool/prompt/read.txt +3 -3
  143. package/src/tool/prompt/sysinfo.txt +29 -0
  144. package/src/tool/prompt/task.txt +66 -4
  145. package/src/tool/prompt/write.txt +2 -2
  146. package/src/tool/question-prompt.mjs +99 -93
  147. package/src/tool/registry.mjs +1701 -1343
  148. package/src/tool/task-tool.mjs +14 -6
  149. package/src/ui/activity-renderer.mjs +667 -664
  150. package/src/ui/repl-background-panel.mjs +7 -0
  151. package/src/ui/repl-capability-panel.mjs +9 -0
  152. package/src/ui/repl-dashboard.mjs +54 -4
  153. package/src/ui/repl-help.mjs +110 -0
  154. package/src/ui/repl-operator-panel.mjs +12 -0
  155. package/src/ui/repl-route-feedback.mjs +35 -0
  156. package/src/ui/repl-status-view.mjs +76 -0
  157. package/src/ui/repl-task-panel.mjs +5 -0
  158. package/src/ui/repl-transcript-panel.mjs +56 -0
  159. package/src/ui/repl-turn-summary.mjs +135 -0
  160. package/src/usage/pricing.mjs +122 -121
  161. package/src/usage/usage-meter.mjs +1 -0
  162. package/src/util/git.mjs +562 -519
  163. package/src/util/template.mjs +6 -1
@@ -1,212 +1,225 @@
1
- import { execFile } from "node:child_process"
2
- import { mkdir, readdir, stat, rm } from "node:fs/promises"
3
- import path from "node:path"
4
- import { githubReposDir } from "../storage/paths.mjs"
5
-
6
- function exec(cmd, args, opts = {}) {
7
- return new Promise((resolve, reject) => {
8
- execFile(cmd, args, { timeout: 120000, ...opts }, (err, stdout, stderr) => {
9
- if (err) {
10
- err.stderr = stderr
11
- reject(err)
12
- } else {
13
- resolve(stdout.trim())
14
- }
15
- })
16
- })
17
- }
18
-
19
- export function repoLocalPath(fullName) {
20
- const [owner, repo] = fullName.split("/")
21
- return path.join(githubReposDir(), owner, repo)
22
- }
23
-
24
- export async function isLocalRepo(fullName) {
25
- try {
26
- const p = repoLocalPath(fullName)
27
- const s = await stat(path.join(p, ".git"))
28
- return s.isDirectory()
29
- } catch {
30
- return false
31
- }
32
- }
33
-
34
- export async function ensureRepo({ fullName, branch, token }) {
35
- const localPath = repoLocalPath(fullName)
36
- const exists = await isLocalRepo(fullName)
37
-
38
- if (!exists) {
39
- // Clone
40
- await mkdir(path.dirname(localPath), { recursive: true })
41
- const cloneUrl = `https://${token}@github.com/${fullName}.git`
42
- await exec("git", ["clone", "--depth", "1", "-b", branch, "--single-branch", cloneUrl, localPath])
43
- // Remove token from remote URL
44
- const cleanUrl = `https://github.com/${fullName}.git`
45
- await exec("git", ["remote", "set-url", "origin", cleanUrl], { cwd: localPath })
46
- // Store token in git credential for this repo
47
- await configureCredential(localPath, token)
48
- return { path: localPath, isNew: true }
49
- }
50
-
51
- // Existing repo fetch & checkout
52
- await configureCredential(localPath, token)
53
- await exec("git", ["fetch", "origin"], { cwd: localPath })
54
-
55
- // Check if branch exists locally
56
- const localBranchList = await localBranches(localPath)
57
- if (localBranchList.includes(branch)) {
58
- await exec("git", ["checkout", branch], { cwd: localPath })
59
- await exec("git", ["pull", "--ff-only", "origin", branch], { cwd: localPath }).catch(() => {
60
- // pull may fail if diverged, that's ok
61
- })
62
- } else {
63
- // Checkout remote branch
64
- await exec("git", ["checkout", "-b", branch, `origin/${branch}`], { cwd: localPath })
65
- }
66
-
67
- return { path: localPath, isNew: false }
68
- }
69
-
70
- async function configureCredential(repoPath, token) {
71
- // Use store credential helper scoped to this repo
72
- const credentialPath = path.join(repoPath, ".git", "kkcode-credentials")
73
- const { writeFile } = await import("node:fs/promises")
74
- await writeFile(credentialPath, `https://x-access-token:${token}@github.com\n`, "utf8")
75
- await exec("git", ["config", "credential.helper", `store --file="${credentialPath}"`], { cwd: repoPath })
76
- }
77
-
78
- export async function listLocalRepos() {
79
- const root = githubReposDir()
80
- const repos = []
81
- try {
82
- const owners = await readdir(root)
83
- for (const owner of owners) {
84
- const ownerPath = path.join(root, owner)
85
- const ownerStat = await stat(ownerPath).catch(() => null)
86
- if (!ownerStat || !ownerStat.isDirectory()) continue
87
- const names = await readdir(ownerPath)
88
- for (const name of names) {
89
- const gitDir = path.join(ownerPath, name, ".git")
90
- const gitStat = await stat(gitDir).catch(() => null)
91
- if (gitStat && gitStat.isDirectory()) {
92
- repos.push(`${owner}/${name}`)
93
- }
94
- }
95
- }
96
- } catch {
97
- // repos dir doesn't exist yet
98
- }
99
- return repos
100
- }
101
-
102
- export async function localBranches(repoPath) {
103
- try {
104
- const out = await exec("git", ["branch", "--list", "--format=%(refname:short)"], { cwd: repoPath })
105
- return out.split("\n").filter(Boolean)
106
- } catch {
107
- return []
108
- }
109
- }
110
-
111
- export async function removeLocalRepo(fullName) {
112
- const localPath = repoLocalPath(fullName)
113
- try {
114
- await rm(localPath, { recursive: true, force: true })
115
- return true
116
- } catch {
117
- return false
118
- }
119
- }
120
-
121
- export async function syncRepo({ fullName, branch, token }) {
122
- const localPath = repoLocalPath(fullName)
123
- await configureCredential(localPath, token)
124
- await exec("git", ["fetch", "origin"], { cwd: localPath })
125
-
126
- // Check if branch exists locally
127
- const localBranchList = await localBranches(localPath)
128
- if (localBranchList.includes(branch)) {
129
- await exec("git", ["checkout", branch], { cwd: localPath })
130
- await exec("git", ["pull", "--ff-only", "origin", branch], { cwd: localPath }).catch(() => {
131
- // pull may fail if diverged, that's ok
132
- })
133
- } else {
134
- // Checkout remote branch
135
- await exec("git", ["checkout", "-b", branch, `origin/${branch}`], { cwd: localPath })
136
- }
137
-
138
- return { path: localPath }
139
- }
140
-
141
- // Check if there are uncommitted changes
142
- export async function hasLocalChanges(repoPath) {
143
- try {
144
- const status = await exec("git", ["status", "--porcelain"], { cwd: repoPath })
145
- return status.length > 0
146
- } catch {
147
- return false
148
- }
149
- }
150
-
151
- // Get diff stats for display
152
- export async function getDiffStats(repoPath) {
153
- try {
154
- const stats = await exec("git", ["diff", "--stat", "HEAD"], { cwd: repoPath })
155
- return stats
156
- } catch {
157
- return ""
158
- }
159
- }
160
-
161
- // Get changed files list
162
- export async function getChangedFiles(repoPath) {
163
- try {
164
- const output = await exec("git", ["status", "--short"], { cwd: repoPath })
165
- return output.split("\n").filter(Boolean)
166
- } catch {
167
- return []
168
- }
169
- }
170
-
171
- // Commit and push changes
172
- export async function commitAndPush({ repoPath, message, branch, token }) {
173
- await configureCredential(repoPath, token)
174
-
175
- // Add all changes
176
- await exec("git", ["add", "-A"], { cwd: repoPath })
177
-
178
- // Commit
179
- await exec("git", ["commit", "-m", message], { cwd: repoPath })
180
-
181
- // Push
182
- await exec("git", ["push", "origin", branch], { cwd: repoPath })
183
-
184
- return true
185
- }
186
-
187
- // Generate a commit message based on changes
188
- export async function generateCommitMessage(repoPath) {
189
- try {
190
- const files = await getChangedFiles(repoPath)
191
- if (files.length === 0) return "Update files"
192
-
193
- // Count types of changes
194
- const added = files.filter(f => f.startsWith("A") || f.startsWith("??")).length
195
- const modified = files.filter(f => f.startsWith("M")).length
196
- const deleted = files.filter(f => f.startsWith("D")).length
197
-
198
- if (added > 0 && modified === 0 && deleted === 0) {
199
- return added === 1 ? "Add file" : `Add ${added} files`
200
- }
201
- if (modified > 0 && added === 0 && deleted === 0) {
202
- return modified === 1 ? "Update file" : `Update ${modified} files`
203
- }
204
- if (deleted > 0 && added === 0 && modified === 0) {
205
- return deleted === 1 ? "Remove file" : `Remove ${deleted} files`
206
- }
207
-
208
- return `Update files (+${added} ~${modified} -${deleted})`
209
- } catch {
210
- return "Update files"
211
- }
212
- }
1
+ import { execFile } from "node:child_process"
2
+ import { chmod, mkdir, readdir, stat, rm } from "node:fs/promises"
3
+ import path from "node:path"
4
+ import { githubReposDir } from "../storage/paths.mjs"
5
+
6
+ function exec(cmd, args, opts = {}) {
7
+ return new Promise((resolve, reject) => {
8
+ execFile(cmd, args, { timeout: 120000, ...opts }, (err, stdout, stderr) => {
9
+ if (err) {
10
+ err.stderr = stderr
11
+ reject(err)
12
+ } else {
13
+ resolve(stdout.trim())
14
+ }
15
+ })
16
+ })
17
+ }
18
+
19
+ export function repoLocalPath(fullName) {
20
+ const [owner, repo] = fullName.split("/")
21
+ return path.join(githubReposDir(), owner, repo)
22
+ }
23
+
24
+ export async function isLocalRepo(fullName) {
25
+ try {
26
+ const p = repoLocalPath(fullName)
27
+ const s = await stat(path.join(p, ".git"))
28
+ return s.isDirectory()
29
+ } catch {
30
+ return false
31
+ }
32
+ }
33
+
34
+ export async function ensureRepo({ fullName, branch, token }) {
35
+ const localPath = repoLocalPath(fullName)
36
+ const exists = await isLocalRepo(fullName)
37
+
38
+ if (!exists) {
39
+ // Clone
40
+ await mkdir(path.dirname(localPath), { recursive: true })
41
+ const cloneUrl = `https://${token}@github.com/${fullName}.git`
42
+ try {
43
+ await exec("git", ["clone", "--depth", "1", "-b", branch, "--single-branch", cloneUrl, localPath])
44
+ } catch (err) {
45
+ sanitizeTokenFromError(err, token)
46
+ throw err
47
+ }
48
+ // Remove token from remote URL
49
+ const cleanUrl = `https://github.com/${fullName}.git`
50
+ await exec("git", ["remote", "set-url", "origin", cleanUrl], { cwd: localPath })
51
+ // Store token in git credential for this repo
52
+ await configureCredential(localPath, token)
53
+ return { path: localPath, isNew: true }
54
+ }
55
+
56
+ // Existing repo fetch & checkout
57
+ await configureCredential(localPath, token)
58
+ await exec("git", ["fetch", "origin"], { cwd: localPath })
59
+
60
+ // Check if branch exists locally
61
+ const localBranchList = await localBranches(localPath)
62
+ if (localBranchList.includes(branch)) {
63
+ await exec("git", ["checkout", branch], { cwd: localPath })
64
+ await exec("git", ["pull", "--ff-only", "origin", branch], { cwd: localPath }).catch(() => {
65
+ // pull may fail if diverged, that's ok
66
+ })
67
+ } else {
68
+ // Checkout remote branch
69
+ await exec("git", ["checkout", "-b", branch, `origin/${branch}`], { cwd: localPath })
70
+ }
71
+
72
+ return { path: localPath, isNew: false }
73
+ }
74
+
75
+ function sanitizeTokenFromError(err, token) {
76
+ const mask = (s) => s ? s.replace(/https:\/\/[^@\s]+@/g, "https://***@") : s
77
+ if (err.message) err.message = mask(err.message)
78
+ if (err.stderr) err.stderr = mask(err.stderr)
79
+ if (err.stdout) err.stdout = mask(err.stdout)
80
+ }
81
+
82
+ async function configureCredential(repoPath, token) {
83
+ // Use store credential helper scoped to this repo
84
+ const credentialPath = path.join(repoPath, ".git", "kkcode-credentials")
85
+ const { writeFile } = await import("node:fs/promises")
86
+ await writeFile(credentialPath, `https://x-access-token:${token}@github.com\n`, { encoding: "utf8", mode: 0o600 })
87
+ await chmod(credentialPath, 0o600)
88
+ await exec("git", ["config", "credential.helper", `store --file="${credentialPath}"`], { cwd: repoPath })
89
+ }
90
+
91
+ export async function listLocalRepos() {
92
+ const root = githubReposDir()
93
+ const repos = []
94
+ try {
95
+ const owners = await readdir(root)
96
+ for (const owner of owners) {
97
+ const ownerPath = path.join(root, owner)
98
+ const ownerStat = await stat(ownerPath).catch(() => null)
99
+ if (!ownerStat || !ownerStat.isDirectory()) continue
100
+ const names = await readdir(ownerPath)
101
+ for (const name of names) {
102
+ const gitDir = path.join(ownerPath, name, ".git")
103
+ const gitStat = await stat(gitDir).catch(() => null)
104
+ if (gitStat && gitStat.isDirectory()) {
105
+ repos.push(`${owner}/${name}`)
106
+ }
107
+ }
108
+ }
109
+ } catch {
110
+ // repos dir doesn't exist yet
111
+ }
112
+ return repos
113
+ }
114
+
115
+ export async function localBranches(repoPath) {
116
+ try {
117
+ const out = await exec("git", ["branch", "--list", "--format=%(refname:short)"], { cwd: repoPath })
118
+ return out.split("\n").filter(Boolean)
119
+ } catch {
120
+ return []
121
+ }
122
+ }
123
+
124
+ export async function removeLocalRepo(fullName) {
125
+ const localPath = repoLocalPath(fullName)
126
+ try {
127
+ await rm(localPath, { recursive: true, force: true })
128
+ return true
129
+ } catch {
130
+ return false
131
+ }
132
+ }
133
+
134
+ export async function syncRepo({ fullName, branch, token }) {
135
+ const localPath = repoLocalPath(fullName)
136
+ await configureCredential(localPath, token)
137
+ await exec("git", ["fetch", "origin"], { cwd: localPath })
138
+
139
+ // Check if branch exists locally
140
+ const localBranchList = await localBranches(localPath)
141
+ if (localBranchList.includes(branch)) {
142
+ await exec("git", ["checkout", branch], { cwd: localPath })
143
+ await exec("git", ["pull", "--ff-only", "origin", branch], { cwd: localPath }).catch(() => {
144
+ // pull may fail if diverged, that's ok
145
+ })
146
+ } else {
147
+ // Checkout remote branch
148
+ await exec("git", ["checkout", "-b", branch, `origin/${branch}`], { cwd: localPath })
149
+ }
150
+
151
+ return { path: localPath }
152
+ }
153
+
154
+ // Check if there are uncommitted changes
155
+ export async function hasLocalChanges(repoPath) {
156
+ try {
157
+ const status = await exec("git", ["status", "--porcelain"], { cwd: repoPath })
158
+ return status.length > 0
159
+ } catch {
160
+ return false
161
+ }
162
+ }
163
+
164
+ // Get diff stats for display
165
+ export async function getDiffStats(repoPath) {
166
+ try {
167
+ const stats = await exec("git", ["diff", "--stat", "HEAD"], { cwd: repoPath })
168
+ return stats
169
+ } catch {
170
+ return ""
171
+ }
172
+ }
173
+
174
+ // Get changed files list
175
+ export async function getChangedFiles(repoPath) {
176
+ try {
177
+ const output = await exec("git", ["status", "--short"], { cwd: repoPath })
178
+ return output.split("\n").filter(Boolean)
179
+ } catch {
180
+ return []
181
+ }
182
+ }
183
+
184
+ // Commit and push changes
185
+ export async function commitAndPush({ repoPath, message, branch, token }) {
186
+ await configureCredential(repoPath, token)
187
+
188
+ // Add all changes
189
+ await exec("git", ["add", "-A"], { cwd: repoPath })
190
+
191
+ // Commit
192
+ await exec("git", ["commit", "-m", message], { cwd: repoPath })
193
+
194
+ // Push
195
+ await exec("git", ["push", "origin", branch], { cwd: repoPath })
196
+
197
+ return true
198
+ }
199
+
200
+ // Generate a commit message based on changes
201
+ export async function generateCommitMessage(repoPath) {
202
+ try {
203
+ const files = await getChangedFiles(repoPath)
204
+ if (files.length === 0) return "Update files"
205
+
206
+ // Count types of changes
207
+ const added = files.filter(f => f.startsWith("A") || f.startsWith("??")).length
208
+ const modified = files.filter(f => f.startsWith("M")).length
209
+ const deleted = files.filter(f => f.startsWith("D")).length
210
+
211
+ if (added > 0 && modified === 0 && deleted === 0) {
212
+ return added === 1 ? "Add file" : `Add ${added} files`
213
+ }
214
+ if (modified > 0 && added === 0 && deleted === 0) {
215
+ return modified === 1 ? "Update file" : `Update ${modified} files`
216
+ }
217
+ if (deleted > 0 && added === 0 && modified === 0) {
218
+ return deleted === 1 ? "Remove file" : `Remove ${deleted} files`
219
+ }
220
+
221
+ return `Update files (+${added} ~${modified} -${deleted})`
222
+ } catch {
223
+ return "Update files"
224
+ }
225
+ }
package/src/index.mjs CHANGED
@@ -1,82 +1,84 @@
1
- #!/usr/bin/env node
2
- import { Command } from "commander"
3
- import { createThemeCommand } from "./commands/theme.mjs"
4
- import { createUsageCommand } from "./commands/usage.mjs"
5
- import { createReviewCommand } from "./commands/review.mjs"
6
- import { createSessionCommand } from "./commands/session.mjs"
7
- import { createChatCommand } from "./commands/chat.mjs"
8
- import { createAgentCommand } from "./commands/agent.mjs"
9
- import { createMcpCommand } from "./commands/mcp.mjs"
10
- import { createPermissionCommand } from "./commands/permission.mjs"
11
- import { createDoctorCommand } from "./commands/doctor.mjs"
12
- import { createConfigCommand } from "./commands/config.mjs"
13
- import { createPromptCommand } from "./commands/prompt.mjs"
14
- import { createLongagentCommand } from "./commands/longagent.mjs"
15
- import { createHookCommand } from "./commands/hook.mjs"
16
- import { createCommandCommand } from "./commands/command.mjs"
17
- import { createRuleCommand } from "./commands/rule.mjs"
18
- import { createBackgroundCommand } from "./commands/background.mjs"
19
- import { createInitCommand } from "./commands/init.mjs"
20
- import { createAuditCommand } from "./commands/audit.mjs"
21
- import { startRepl } from "./repl.mjs"
22
-
23
- async function main() {
24
- const hasTrust = process.argv.includes("--trust")
25
- const hasGithub = process.argv.includes("--github")
26
-
27
- if (hasGithub) {
28
- const githubArgIndex = process.argv.indexOf("--github")
29
- const nextArg = process.argv[githubArgIndex + 1]
30
-
31
- if (nextArg === "logout") {
32
- const { logout } = await import("./github/auth.mjs")
33
- const success = await logout()
34
- if (success) {
35
- console.log("✓ 已登出 GitHub 账户")
36
- } else {
37
- console.log("⚠ 没有已登录的 GitHub 账户")
38
- }
39
- return
40
- }
41
-
42
- const { runGitHubFlow, promptPushChanges } = await import("./github/flow.mjs")
43
- const result = await runGitHubFlow()
44
- process.chdir(result.cwd)
45
- await startRepl({ trust: hasTrust })
46
- // After REPL exits, ask user if they want to push changes
47
- await promptPushChanges(result)
48
- return
49
- }
50
-
51
- if (process.argv.length <= 2 || (process.argv.length === 3 && hasTrust)) {
52
- await startRepl({ trust: hasTrust })
53
- return
54
- }
55
-
56
- const program = new Command()
57
- program.name("kkcode").description("kkcode CLI").version("0.1.6")
58
- program.addCommand(createChatCommand())
59
- program.addCommand(createThemeCommand())
60
- program.addCommand(createUsageCommand())
61
- program.addCommand(createReviewCommand())
62
- program.addCommand(createAgentCommand())
63
- program.addCommand(createMcpCommand())
64
- program.addCommand(createPermissionCommand())
65
- program.addCommand(createDoctorCommand())
66
- program.addCommand(createConfigCommand())
67
- program.addCommand(createSessionCommand())
68
- program.addCommand(createPromptCommand())
69
- program.addCommand(createLongagentCommand())
70
- program.addCommand(createHookCommand())
71
- program.addCommand(createCommandCommand())
72
- program.addCommand(createRuleCommand())
73
- program.addCommand(createBackgroundCommand())
74
- program.addCommand(createAuditCommand())
75
- program.addCommand(createInitCommand())
76
- await program.parseAsync(process.argv)
77
- }
78
-
79
- main().catch((error) => {
80
- console.error(error.message)
81
- process.exit(1)
82
- })
1
+ #!/usr/bin/env node
2
+ import { Command } from "commander"
3
+ import { createThemeCommand } from "./commands/theme.mjs"
4
+ import { createUsageCommand } from "./commands/usage.mjs"
5
+ import { createReviewCommand } from "./commands/review.mjs"
6
+ import { createSessionCommand } from "./commands/session.mjs"
7
+ import { createChatCommand } from "./commands/chat.mjs"
8
+ import { createAgentCommand } from "./commands/agent.mjs"
9
+ import { createMcpCommand } from "./commands/mcp.mjs"
10
+ import { createPermissionCommand } from "./commands/permission.mjs"
11
+ import { createDoctorCommand } from "./commands/doctor.mjs"
12
+ import { createConfigCommand } from "./commands/config.mjs"
13
+ import { createPromptCommand } from "./commands/prompt.mjs"
14
+ import { createLongagentCommand } from "./commands/longagent.mjs"
15
+ import { createHookCommand } from "./commands/hook.mjs"
16
+ import { createCommandCommand } from "./commands/command.mjs"
17
+ import { createRuleCommand } from "./commands/rule.mjs"
18
+ import { createBackgroundCommand } from "./commands/background.mjs"
19
+ import { createInitCommand } from "./commands/init.mjs"
20
+ import { createAuditCommand } from "./commands/audit.mjs"
21
+ import { createSkillCommand } from "./commands/skill.mjs"
22
+ import { startRepl } from "./repl.mjs"
23
+
24
+ async function main() {
25
+ const hasTrust = process.argv.includes("--trust")
26
+ const hasGithub = process.argv.includes("--github")
27
+
28
+ if (hasGithub) {
29
+ const githubArgIndex = process.argv.indexOf("--github")
30
+ const nextArg = process.argv[githubArgIndex + 1]
31
+
32
+ if (nextArg === "logout") {
33
+ const { logout } = await import("./github/auth.mjs")
34
+ const success = await logout()
35
+ if (success) {
36
+ console.log("✓ 已登出 GitHub 账户")
37
+ } else {
38
+ console.log("⚠ 没有已登录的 GitHub 账户")
39
+ }
40
+ return
41
+ }
42
+
43
+ const { runGitHubFlow, promptPushChanges } = await import("./github/flow.mjs")
44
+ const result = await runGitHubFlow()
45
+ process.chdir(result.cwd)
46
+ await startRepl({ trust: hasTrust })
47
+ // After REPL exits, ask user if they want to push changes
48
+ await promptPushChanges(result)
49
+ return
50
+ }
51
+
52
+ if (process.argv.length <= 2 || (process.argv.length === 3 && hasTrust)) {
53
+ await startRepl({ trust: hasTrust })
54
+ return
55
+ }
56
+
57
+ const program = new Command()
58
+ program.name("kkcode").description("kkcode CLI").version("0.2.1")
59
+ program.addCommand(createChatCommand())
60
+ program.addCommand(createThemeCommand())
61
+ program.addCommand(createUsageCommand())
62
+ program.addCommand(createReviewCommand())
63
+ program.addCommand(createAgentCommand())
64
+ program.addCommand(createMcpCommand())
65
+ program.addCommand(createPermissionCommand())
66
+ program.addCommand(createDoctorCommand())
67
+ program.addCommand(createConfigCommand())
68
+ program.addCommand(createSessionCommand())
69
+ program.addCommand(createPromptCommand())
70
+ program.addCommand(createLongagentCommand())
71
+ program.addCommand(createHookCommand())
72
+ program.addCommand(createCommandCommand())
73
+ program.addCommand(createRuleCommand())
74
+ program.addCommand(createBackgroundCommand())
75
+ program.addCommand(createAuditCommand())
76
+ program.addCommand(createInitCommand())
77
+ program.addCommand(createSkillCommand())
78
+ await program.parseAsync(process.argv)
79
+ }
80
+
81
+ main().catch((error) => {
82
+ console.error(error.message)
83
+ process.exit(1)
84
+ })