@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.
- package/LICENSE +674 -674
- package/README.md +452 -387
- package/package.json +50 -46
- package/src/agent/agent.mjs +228 -220
- package/src/agent/custom-agent-loader.mjs +6 -3
- package/src/agent/generator.mjs +2 -2
- package/src/agent/prompt/assistant.txt +12 -0
- package/src/agent/prompt/bug-hunter.txt +89 -89
- package/src/agent/prompt/frontend-designer.txt +58 -58
- package/src/agent/prompt/guide.txt +1 -1
- package/src/agent/prompt/longagent-blueprint-agent.txt +83 -83
- package/src/agent/prompt/longagent-coding-agent.txt +37 -37
- package/src/agent/prompt/longagent-debugging-agent.txt +46 -46
- package/src/agent/prompt/longagent-preview-agent.txt +63 -63
- package/src/command/custom-commands.mjs +2 -2
- package/src/commands/agent.mjs +1 -1
- package/src/commands/background.mjs +145 -4
- package/src/commands/chat.mjs +117 -76
- package/src/commands/config.mjs +148 -1
- package/src/commands/doctor.mjs +30 -6
- package/src/commands/init.mjs +32 -6
- package/src/commands/longagent.mjs +117 -0
- package/src/commands/mcp.mjs +275 -43
- package/src/commands/permission.mjs +1 -1
- package/src/commands/session.mjs +195 -140
- package/src/commands/skill.mjs +63 -0
- package/src/commands/theme.mjs +1 -1
- package/src/config/defaults.mjs +280 -260
- package/src/config/import-config.mjs +1 -1
- package/src/config/load-config.mjs +61 -4
- package/src/config/schema.mjs +591 -574
- package/src/context.mjs +4 -1
- package/src/core/constants.mjs +97 -91
- package/src/core/types.mjs +1 -1
- package/src/github/api.mjs +78 -78
- package/src/github/auth.mjs +294 -286
- package/src/github/flow.mjs +298 -298
- package/src/github/workspace.mjs +225 -212
- package/src/index.mjs +84 -82
- package/src/knowledge/frontend-aesthetics.txt +38 -38
- package/src/mcp/client-http.mjs +139 -141
- package/src/mcp/client-sse.mjs +297 -288
- package/src/mcp/client-stdio.mjs +534 -533
- package/src/mcp/constants.mjs +2 -2
- package/src/mcp/registry.mjs +498 -479
- package/src/mcp/stdio-framing.mjs +135 -133
- package/src/mcp/tool-result.mjs +24 -24
- package/src/observability/edit-diagnostics.mjs +449 -0
- package/src/observability/index.mjs +42 -42
- package/src/observability/metrics.mjs +165 -137
- package/src/observability/tracer.mjs +137 -137
- package/src/onboarding.mjs +209 -0
- package/src/orchestration/background-manager.mjs +567 -372
- package/src/orchestration/background-worker.mjs +419 -305
- package/src/orchestration/interruption-reason.mjs +21 -0
- package/src/orchestration/longagent-manager.mjs +197 -171
- package/src/orchestration/stage-scheduler.mjs +733 -728
- package/src/orchestration/subagent-router.mjs +7 -1
- package/src/orchestration/task-scheduler.mjs +219 -7
- package/src/permission/engine.mjs +1 -1
- package/src/permission/exec-policy.mjs +370 -370
- package/src/permission/file-edit-policy.mjs +108 -0
- package/src/permission/prompt.mjs +1 -1
- package/src/permission/rules.mjs +116 -7
- package/src/plugin/builtin-hooks/post-edit-format.mjs +2 -1
- package/src/plugin/builtin-hooks/post-edit-typecheck.mjs +104 -40
- package/src/plugin/hook-bus.mjs +19 -5
- package/src/plugin/manifest-loader.mjs +222 -0
- package/src/provider/anthropic.mjs +396 -390
- package/src/provider/ollama.mjs +7 -1
- package/src/provider/openai.mjs +382 -340
- package/src/provider/retry-policy.mjs +74 -68
- package/src/provider/router.mjs +242 -241
- package/src/provider/sse.mjs +104 -104
- package/src/provider/wizard.mjs +556 -0
- package/src/repl/capability-facade.mjs +30 -0
- package/src/repl/command-surface.mjs +23 -0
- package/src/repl/controller-entry.mjs +40 -0
- package/src/repl/core-shell.mjs +208 -0
- package/src/repl/dialog-router.mjs +87 -0
- package/src/repl/input-engine.mjs +76 -0
- package/src/repl/keymap.mjs +7 -0
- package/src/repl/operator-surface.mjs +15 -0
- package/src/repl/permission-flow.mjs +49 -0
- package/src/repl/runtime-facade.mjs +36 -0
- package/src/repl/slash-router.mjs +62 -0
- package/src/repl/state-store.mjs +29 -0
- package/src/repl/turn-controller.mjs +58 -0
- package/src/repl/verification.mjs +23 -0
- package/src/repl.mjs +3368 -2981
- package/src/rules/load-rules.mjs +3 -3
- package/src/runtime.mjs +1 -1
- package/src/session/agent-transaction.mjs +86 -0
- package/src/session/checkpoint.mjs +302 -302
- package/src/session/compaction.mjs +298 -298
- package/src/session/engine.mjs +417 -232
- package/src/session/longagent-4stage.mjs +467 -460
- package/src/session/longagent-hybrid.mjs +1344 -1097
- package/src/session/longagent-plan.mjs +376 -365
- package/src/session/longagent-project-memory.mjs +53 -53
- package/src/session/longagent-scaffold.mjs +291 -291
- package/src/session/longagent-task-bus.mjs +138 -54
- package/src/session/longagent-utils.mjs +828 -472
- package/src/session/longagent.mjs +911 -900
- package/src/session/loop.mjs +1005 -930
- package/src/session/prompt/agent.txt +25 -25
- package/src/session/prompt/anthropic.txt +150 -150
- package/src/session/prompt/beast.txt +1 -1
- package/src/session/prompt/plan.txt +31 -31
- package/src/session/prompt/qwen.txt +46 -46
- package/src/session/recovery.mjs +21 -0
- package/src/session/rollback.mjs +196 -195
- package/src/session/routing-observability.mjs +72 -0
- package/src/session/runtime-state.mjs +47 -0
- package/src/session/store.mjs +523 -519
- package/src/session/system-prompt.mjs +308 -273
- package/src/session/task-validator.mjs +267 -267
- package/src/session/usability-gates.mjs +2 -2
- package/src/skill/builtin/commit.mjs +64 -64
- package/src/skill/builtin/design.mjs +76 -76
- package/src/skill/generator.mjs +18 -2
- package/src/skill/registry.mjs +642 -390
- package/src/storage/audit-store.mjs +18 -11
- package/src/storage/event-log.mjs +7 -1
- package/src/storage/ghost-commit-store.mjs +243 -245
- package/src/storage/paths.mjs +13 -0
- package/src/theme/default-theme.mjs +1 -1
- package/src/theme/markdown.mjs +4 -0
- package/src/theme/schema.mjs +1 -1
- package/src/theme/status-bar.mjs +162 -158
- package/src/tool/audit-wrapper.mjs +18 -2
- package/src/tool/edit-transaction.mjs +23 -0
- package/src/tool/executor.mjs +26 -1
- package/src/tool/file-read-state.mjs +65 -0
- package/src/tool/git-auto.mjs +526 -526
- package/src/tool/git-full-auto.mjs +487 -478
- package/src/tool/mutation-guard.mjs +54 -0
- package/src/tool/prompt/edit.txt +3 -3
- package/src/tool/prompt/multiedit.txt +1 -0
- package/src/tool/prompt/notebookedit.txt +2 -1
- package/src/tool/prompt/patch.txt +25 -24
- package/src/tool/prompt/read.txt +3 -3
- package/src/tool/prompt/sysinfo.txt +29 -0
- package/src/tool/prompt/task.txt +66 -4
- package/src/tool/prompt/write.txt +2 -2
- package/src/tool/question-prompt.mjs +99 -93
- package/src/tool/registry.mjs +1701 -1343
- package/src/tool/task-tool.mjs +14 -6
- package/src/ui/activity-renderer.mjs +667 -664
- package/src/ui/repl-background-panel.mjs +7 -0
- package/src/ui/repl-capability-panel.mjs +9 -0
- package/src/ui/repl-dashboard.mjs +54 -4
- package/src/ui/repl-help.mjs +110 -0
- package/src/ui/repl-operator-panel.mjs +12 -0
- package/src/ui/repl-route-feedback.mjs +35 -0
- package/src/ui/repl-status-view.mjs +76 -0
- package/src/ui/repl-task-panel.mjs +5 -0
- package/src/ui/repl-transcript-panel.mjs +56 -0
- package/src/ui/repl-turn-summary.mjs +135 -0
- package/src/usage/pricing.mjs +122 -121
- package/src/usage/usage-meter.mjs +1 -0
- package/src/util/git.mjs +562 -519
- package/src/util/template.mjs +6 -1
package/src/github/workspace.mjs
CHANGED
|
@@ -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
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
await exec("git", ["
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
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 {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
program
|
|
58
|
-
program.
|
|
59
|
-
program.addCommand(
|
|
60
|
-
program.addCommand(
|
|
61
|
-
program.addCommand(
|
|
62
|
-
program.addCommand(
|
|
63
|
-
program.addCommand(
|
|
64
|
-
program.addCommand(
|
|
65
|
-
program.addCommand(
|
|
66
|
-
program.addCommand(
|
|
67
|
-
program.addCommand(
|
|
68
|
-
program.addCommand(
|
|
69
|
-
program.addCommand(
|
|
70
|
-
program.addCommand(
|
|
71
|
-
program.addCommand(
|
|
72
|
-
program.addCommand(
|
|
73
|
-
program.addCommand(
|
|
74
|
-
program.addCommand(
|
|
75
|
-
program.addCommand(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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
|
+
})
|