@kkelly-offical/kkcode 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/agent/agent.mjs +220 -211
- package/src/agent/prompt/bug-hunter.txt +90 -0
- package/src/repl.mjs +58 -6
- package/src/session/compaction.mjs +298 -276
- package/src/session/engine.mjs +5 -0
- package/src/session/longagent-hybrid.mjs +21 -5
- package/src/session/longagent.mjs +21 -5
- package/src/session/loop.mjs +65 -40
- package/src/session/prompt/agent.txt +25 -0
- package/src/session/prompt/plan.txt +31 -9
- package/src/session/rollback.mjs +196 -0
- package/src/session/store.mjs +12 -3
- package/src/session/system-prompt.mjs +273 -260
- package/src/tool/question-prompt.mjs +93 -86
|
@@ -1,86 +1,93 @@
|
|
|
1
|
-
import { stdin as input, stdout as output } from "node:process"
|
|
2
|
-
import { createInterface } from "node:readline/promises"
|
|
3
|
-
|
|
4
|
-
let customPromptHandler = null
|
|
5
|
-
|
|
6
|
-
export function setQuestionPromptHandler(handler) {
|
|
7
|
-
customPromptHandler = typeof handler === "function" ? handler : null
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export async function askQuestionInteractive({ questions }) {
|
|
11
|
-
if (!Array.isArray(questions) || questions.length === 0) {
|
|
12
|
-
return {}
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// 1. TUI handler (registered by repl.mjs)
|
|
16
|
-
if (customPromptHandler) {
|
|
17
|
-
const answers = await customPromptHandler({ questions })
|
|
18
|
-
if (answers && typeof answers === "object") return answers
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// 2. Non-TTY: return empty answers
|
|
22
|
-
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
23
|
-
return Object.fromEntries(questions.map((q) => [q.id, ""]))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// 3. TTY fallback: readline sequential Q&A
|
|
27
|
-
const rl = createInterface({ input, output })
|
|
28
|
-
const answers = {}
|
|
29
|
-
try {
|
|
30
|
-
for (const q of questions) {
|
|
31
|
-
console.log("")
|
|
32
|
-
console.log(` ${q.text}`)
|
|
33
|
-
if (q.description) console.log(` ${q.description}`)
|
|
34
|
-
const options = Array.isArray(q.options) ? q.options : []
|
|
35
|
-
if (options.length) {
|
|
36
|
-
for (let i = 0; i < options.length; i++) {
|
|
37
|
-
const opt = options[i]
|
|
38
|
-
console.log(` ${i + 1}. ${opt.label}`)
|
|
39
|
-
if (opt.description) console.log(` ${opt.description}`)
|
|
40
|
-
}
|
|
41
|
-
if (q.allowCustom !== false) {
|
|
42
|
-
console.log(` ${options.length + 1}. Custom...`)
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
const raw = (await rl.question(" > ")).trim()
|
|
46
|
-
if (options.length) {
|
|
47
|
-
const idx = parseInt(raw, 10)
|
|
48
|
-
if (idx >= 1 && idx <= options.length) {
|
|
49
|
-
const chosen = options[idx - 1]
|
|
50
|
-
answers[q.id] = chosen.value || chosen.label
|
|
51
|
-
} else {
|
|
52
|
-
answers[q.id] = raw
|
|
53
|
-
}
|
|
54
|
-
} else {
|
|
55
|
-
answers[q.id] = raw
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
} finally {
|
|
59
|
-
rl.close()
|
|
60
|
-
}
|
|
61
|
-
return answers
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export async function askPlanApproval({ plan, files = [] }) {
|
|
65
|
-
const fileList = files.length ? `\nFiles to modify:\n${files.map(f => ` - ${f}`).join("\n")}` : ""
|
|
66
|
-
const questions = [
|
|
67
|
-
{
|
|
68
|
-
id: "plan_approval",
|
|
69
|
-
text: `Plan Review`,
|
|
70
|
-
description: `${plan}${fileList}`,
|
|
71
|
-
options: [
|
|
72
|
-
{ label: "Approve", value: "approve", description: "Proceed with this plan" },
|
|
73
|
-
{ label: "
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
}
|
|
1
|
+
import { stdin as input, stdout as output } from "node:process"
|
|
2
|
+
import { createInterface } from "node:readline/promises"
|
|
3
|
+
|
|
4
|
+
let customPromptHandler = null
|
|
5
|
+
|
|
6
|
+
export function setQuestionPromptHandler(handler) {
|
|
7
|
+
customPromptHandler = typeof handler === "function" ? handler : null
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export async function askQuestionInteractive({ questions }) {
|
|
11
|
+
if (!Array.isArray(questions) || questions.length === 0) {
|
|
12
|
+
return {}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 1. TUI handler (registered by repl.mjs)
|
|
16
|
+
if (customPromptHandler) {
|
|
17
|
+
const answers = await customPromptHandler({ questions })
|
|
18
|
+
if (answers && typeof answers === "object") return answers
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// 2. Non-TTY: return empty answers
|
|
22
|
+
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
23
|
+
return Object.fromEntries(questions.map((q) => [q.id, ""]))
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// 3. TTY fallback: readline sequential Q&A
|
|
27
|
+
const rl = createInterface({ input, output })
|
|
28
|
+
const answers = {}
|
|
29
|
+
try {
|
|
30
|
+
for (const q of questions) {
|
|
31
|
+
console.log("")
|
|
32
|
+
console.log(` ${q.text}`)
|
|
33
|
+
if (q.description) console.log(` ${q.description}`)
|
|
34
|
+
const options = Array.isArray(q.options) ? q.options : []
|
|
35
|
+
if (options.length) {
|
|
36
|
+
for (let i = 0; i < options.length; i++) {
|
|
37
|
+
const opt = options[i]
|
|
38
|
+
console.log(` ${i + 1}. ${opt.label}`)
|
|
39
|
+
if (opt.description) console.log(` ${opt.description}`)
|
|
40
|
+
}
|
|
41
|
+
if (q.allowCustom !== false) {
|
|
42
|
+
console.log(` ${options.length + 1}. Custom...`)
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const raw = (await rl.question(" > ")).trim()
|
|
46
|
+
if (options.length) {
|
|
47
|
+
const idx = parseInt(raw, 10)
|
|
48
|
+
if (idx >= 1 && idx <= options.length) {
|
|
49
|
+
const chosen = options[idx - 1]
|
|
50
|
+
answers[q.id] = chosen.value || chosen.label
|
|
51
|
+
} else {
|
|
52
|
+
answers[q.id] = raw
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
answers[q.id] = raw
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} finally {
|
|
59
|
+
rl.close()
|
|
60
|
+
}
|
|
61
|
+
return answers
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export async function askPlanApproval({ plan, files = [] }) {
|
|
65
|
+
const fileList = files.length ? `\nFiles to modify:\n${files.map(f => ` - ${f}`).join("\n")}` : ""
|
|
66
|
+
const questions = [
|
|
67
|
+
{
|
|
68
|
+
id: "plan_approval",
|
|
69
|
+
text: `Plan Review`,
|
|
70
|
+
description: `${plan}${fileList}`,
|
|
71
|
+
options: [
|
|
72
|
+
{ label: "Approve", value: "approve", description: "Proceed with this plan" },
|
|
73
|
+
{ label: "Request Changes", value: "changes", description: "Revise and resubmit with feedback" },
|
|
74
|
+
{ label: "Reject", value: "reject", description: "Cancel this plan entirely" }
|
|
75
|
+
],
|
|
76
|
+
multi: false,
|
|
77
|
+
allowCustom: true
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
const answers = await askQuestionInteractive({ questions })
|
|
81
|
+
const answer = String(answers.plan_approval || "").trim().toLowerCase()
|
|
82
|
+
if (answer === "approve" || answer === "1") {
|
|
83
|
+
return { approved: true, requestChanges: false, feedback: "" }
|
|
84
|
+
}
|
|
85
|
+
if (answer === "changes" || answer === "2") {
|
|
86
|
+
return { approved: false, requestChanges: true, feedback: "" }
|
|
87
|
+
}
|
|
88
|
+
if (answer === "reject" || answer === "3") {
|
|
89
|
+
return { approved: false, requestChanges: false, feedback: "" }
|
|
90
|
+
}
|
|
91
|
+
// Custom text input: treat as "request changes" with the text as feedback
|
|
92
|
+
return { approved: false, requestChanges: true, feedback: answer }
|
|
93
|
+
}
|