@kkelly-offical/kkcode 0.1.3 → 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.
Files changed (66) hide show
  1. package/README.md +110 -172
  2. package/package.json +46 -46
  3. package/src/agent/agent.mjs +220 -170
  4. package/src/agent/prompt/bug-hunter.txt +90 -0
  5. package/src/agent/prompt/frontend-designer.txt +58 -0
  6. package/src/agent/prompt/longagent-blueprint-agent.txt +83 -0
  7. package/src/agent/prompt/longagent-coding-agent.txt +37 -0
  8. package/src/agent/prompt/longagent-debugging-agent.txt +46 -0
  9. package/src/agent/prompt/longagent-preview-agent.txt +63 -0
  10. package/src/config/defaults.mjs +260 -195
  11. package/src/config/schema.mjs +71 -6
  12. package/src/core/constants.mjs +91 -46
  13. package/src/index.mjs +1 -1
  14. package/src/knowledge/frontend-aesthetics.txt +39 -0
  15. package/src/knowledge/loader.mjs +2 -1
  16. package/src/knowledge/tailwind.txt +12 -3
  17. package/src/mcp/client-http.mjs +141 -157
  18. package/src/mcp/client-sse.mjs +288 -286
  19. package/src/mcp/client-stdio.mjs +533 -451
  20. package/src/mcp/constants.mjs +2 -0
  21. package/src/mcp/registry.mjs +479 -394
  22. package/src/mcp/stdio-framing.mjs +133 -127
  23. package/src/mcp/tool-result.mjs +24 -0
  24. package/src/observability/index.mjs +42 -0
  25. package/src/observability/metrics.mjs +137 -0
  26. package/src/observability/tracer.mjs +137 -0
  27. package/src/orchestration/background-manager.mjs +372 -358
  28. package/src/orchestration/background-worker.mjs +305 -245
  29. package/src/orchestration/longagent-manager.mjs +171 -116
  30. package/src/orchestration/stage-scheduler.mjs +728 -489
  31. package/src/permission/exec-policy.mjs +9 -11
  32. package/src/provider/anthropic.mjs +1 -0
  33. package/src/provider/openai.mjs +340 -339
  34. package/src/provider/retry-policy.mjs +68 -68
  35. package/src/provider/router.mjs +241 -228
  36. package/src/provider/sse.mjs +104 -91
  37. package/src/repl.mjs +59 -7
  38. package/src/session/checkpoint.mjs +66 -3
  39. package/src/session/compaction.mjs +298 -276
  40. package/src/session/engine.mjs +232 -225
  41. package/src/session/longagent-4stage.mjs +460 -0
  42. package/src/session/longagent-hybrid.mjs +1097 -0
  43. package/src/session/longagent-plan.mjs +365 -329
  44. package/src/session/longagent-project-memory.mjs +53 -0
  45. package/src/session/longagent-scaffold.mjs +291 -100
  46. package/src/session/longagent-task-bus.mjs +54 -0
  47. package/src/session/longagent-utils.mjs +472 -0
  48. package/src/session/longagent.mjs +900 -1462
  49. package/src/session/loop.mjs +65 -40
  50. package/src/session/project-context.mjs +30 -0
  51. package/src/session/prompt/agent.txt +25 -0
  52. package/src/session/prompt/plan.txt +31 -9
  53. package/src/session/rollback.mjs +196 -0
  54. package/src/session/store.mjs +519 -503
  55. package/src/session/system-prompt.mjs +273 -260
  56. package/src/session/task-validator.mjs +4 -3
  57. package/src/skill/builtin/design.mjs +76 -0
  58. package/src/skill/builtin/frontend.mjs +8 -0
  59. package/src/skill/registry.mjs +390 -336
  60. package/src/storage/ghost-commit-store.mjs +18 -8
  61. package/src/tool/executor.mjs +11 -0
  62. package/src/tool/git-auto.mjs +0 -19
  63. package/src/tool/question-prompt.mjs +93 -86
  64. package/src/tool/registry.mjs +71 -37
  65. package/src/ui/activity-renderer.mjs +664 -410
  66. package/src/util/git.mjs +23 -0
@@ -1,157 +1,141 @@
1
- import { McpError } from "../core/errors.mjs"
2
- import { EventBus } from "../core/events.mjs"
3
- import { EVENT_TYPES } from "../core/constants.mjs"
4
-
5
- function timeoutSignal(ms, parentSignal = null) {
6
- const own = AbortSignal.timeout(ms)
7
- if (!parentSignal) return own
8
- return AbortSignal.any([parentSignal, own])
9
- }
10
-
11
- function classifyHttpError(error, status = null) {
12
- const msg = String(error?.message || error || "")
13
- if (msg.includes("AbortError") || msg.includes("timeout") || msg.includes("abort")) return "timeout"
14
- if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) return "connection_refused"
15
- if (status && status >= 500) return "server_crash"
16
- if (status && status >= 400) return "bad_response"
17
- return "unknown"
18
- }
19
-
20
- function normalizeToolResult(result, serverName, toolName) {
21
- if (result?.isError) {
22
- const text = Array.isArray(result.content)
23
- ? result.content.map((item) => item?.text || "").join("\n").trim()
24
- : ""
25
- throw new McpError(text || "mcp tool returned isError", {
26
- reason: "bad_response",
27
- server: serverName,
28
- action: `tools/call:${toolName}`,
29
- phase: "request"
30
- })
31
- }
32
- const content = Array.isArray(result?.content) ? result.content : null
33
- const contentText = content
34
- ? content.map((item) => (typeof item?.text === "string" ? item.text : "")).join("\n").trim()
35
- : ""
36
- const output =
37
- contentText ||
38
- (typeof result?.output === "string" ? result.output : "") ||
39
- (typeof result === "string" ? result : JSON.stringify(result))
40
- return content ? { output, raw: result, content } : { output, raw: result }
41
- }
42
-
43
- async function requestJson({ serverName, method, url, body = null, timeoutMs = 10000, headers = {}, signal = null }) {
44
- const action = method === "GET" ? url.split("/").pop() : body?.args ? "call_tool" : "request"
45
- const startedAt = Date.now()
46
- let status = null
47
-
48
- try {
49
- const res = await fetch(url, {
50
- method,
51
- headers: {
52
- "content-type": "application/json",
53
- ...headers
54
- },
55
- body: body ? JSON.stringify(body) : undefined,
56
- signal: timeoutSignal(timeoutMs, signal)
57
- })
58
-
59
- status = res.status
60
- const elapsed = Date.now() - startedAt
61
-
62
- EventBus.emit({
63
- type: EVENT_TYPES.MCP_REQUEST,
64
- payload: { server: serverName, action, method, elapsed, status }
65
- }).catch(() => {})
66
-
67
- if (!res.ok) {
68
- const text = await res.text().catch(() => "")
69
- const reason = classifyHttpError(null, status)
70
- throw new McpError(
71
- `mcp server "${serverName}" HTTP ${status} on ${method} ${url}: ${text.slice(0, 500)}`,
72
- { reason, server: serverName, action, phase: "request", statusCode: status }
73
- )
74
- }
75
- return res.json().catch(() => ({}))
76
- } catch (error) {
77
- if (error instanceof McpError) throw error
78
- const reason = classifyHttpError(error, status)
79
- throw new McpError(
80
- `mcp server "${serverName}" ${reason} on ${method} ${url}: ${error.message}`,
81
- { reason, server: serverName, action, phase: "request", statusCode: status }
82
- )
83
- }
84
- }
85
-
86
- export function createHttpMcpClient(serverName, config) {
87
- const baseUrl = String(config.url || "").replace(/\/$/, "")
88
- const timeoutMs = Number(config.timeout_ms || 10000)
89
- const headers = config.headers || {}
90
-
91
- return {
92
- serverName,
93
- transport: "http",
94
- async health() {
95
- try {
96
- await requestJson({ serverName, method: "GET", url: `${baseUrl}/health`, timeoutMs, headers })
97
- return { ok: true }
98
- } catch (error) {
99
- return { ok: false, error: error.message, reason: error.reason || "unknown" }
100
- }
101
- },
102
- async listTools() {
103
- const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/tools`, timeoutMs, headers })
104
- return Array.isArray(out?.tools) ? out.tools : []
105
- },
106
- async listPrompts() {
107
- try {
108
- const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/prompts`, timeoutMs, headers })
109
- return Array.isArray(out?.prompts) ? out.prompts : []
110
- } catch {
111
- return []
112
- }
113
- },
114
- async getPrompt(name, args = {}) {
115
- return requestJson({
116
- serverName,
117
- method: "POST",
118
- url: `${baseUrl}/prompts/${encodeURIComponent(name)}`,
119
- body: { arguments: args },
120
- timeoutMs,
121
- headers
122
- })
123
- },
124
- async listResources() {
125
- try {
126
- const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/resources`, timeoutMs, headers })
127
- return Array.isArray(out?.resources) ? out.resources : []
128
- } catch (error) {
129
- if (error.reason === "server_crash" || error.reason === "timeout") throw error
130
- return []
131
- }
132
- },
133
- async listTemplates() {
134
- try {
135
- const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/templates`, timeoutMs, headers })
136
- return Array.isArray(out?.templates) ? out.templates : []
137
- } catch {
138
- return []
139
- }
140
- },
141
- async callTool(name, args = {}, signal = null) {
142
- const result = await requestJson({
143
- serverName,
144
- method: "POST",
145
- url: `${baseUrl}/tools/${encodeURIComponent(name)}`,
146
- body: { args },
147
- timeoutMs,
148
- headers,
149
- signal
150
- })
151
- return normalizeToolResult(result, serverName, name)
152
- },
153
- shutdown() {
154
- // HTTP client is stateless — no persistent connections to clean up
155
- }
156
- }
157
- }
1
+ import { McpError } from "../core/errors.mjs"
2
+ import { EventBus } from "../core/events.mjs"
3
+ import { EVENT_TYPES } from "../core/constants.mjs"
4
+ import { normalizeToolResult } from "./tool-result.mjs"
5
+
6
+ function timeoutSignal(ms, parentSignal = null) {
7
+ const own = AbortSignal.timeout(ms)
8
+ if (!parentSignal) return own
9
+ return AbortSignal.any([parentSignal, own])
10
+ }
11
+
12
+ function classifyHttpError(error, status = null) {
13
+ const msg = String(error?.message || error || "")
14
+ if (msg.includes("AbortError") || msg.includes("timeout") || msg.includes("abort")) return "timeout"
15
+ if (msg.includes("ECONNREFUSED") || msg.includes("fetch failed")) return "connection_refused"
16
+ if (status && status >= 500) return "server_crash"
17
+ if (status && status >= 400) return "bad_response"
18
+ return "unknown"
19
+ }
20
+
21
+ async function requestJson({ serverName, method, url, body = null, timeoutMs = 10000, headers = {}, signal = null }) {
22
+ const action = method === "GET" ? url.split("/").pop() : body?.args ? "call_tool" : "request"
23
+ const startedAt = Date.now()
24
+ let status = null
25
+
26
+ try {
27
+ const res = await fetch(url, {
28
+ method,
29
+ headers: {
30
+ "content-type": "application/json",
31
+ ...headers
32
+ },
33
+ body: body ? JSON.stringify(body) : undefined,
34
+ signal: timeoutSignal(timeoutMs, signal)
35
+ })
36
+
37
+ status = res.status
38
+ const elapsed = Date.now() - startedAt
39
+
40
+ EventBus.emit({
41
+ type: EVENT_TYPES.MCP_REQUEST,
42
+ payload: { server: serverName, action, method, elapsed, status }
43
+ }).catch(() => {})
44
+
45
+ if (!res.ok) {
46
+ const text = await res.text().catch(() => "")
47
+ const reason = classifyHttpError(null, status)
48
+ throw new McpError(
49
+ `mcp server "${serverName}" HTTP ${status} on ${method} ${url}: ${text.slice(0, 500)}`,
50
+ { reason, server: serverName, action, phase: "request", statusCode: status }
51
+ )
52
+ }
53
+ return res.json().catch((parseErr) => {
54
+ const action = body?.args ? "call_tool" : "request"
55
+ EventBus.emit({
56
+ type: EVENT_TYPES.MCP_REQUEST,
57
+ payload: { server: serverName, action, warning: "malformed_json_response", detail: parseErr.message }
58
+ }).catch(() => {})
59
+ return {}
60
+ })
61
+ } catch (error) {
62
+ if (error instanceof McpError) throw error
63
+ const reason = classifyHttpError(error, status)
64
+ throw new McpError(
65
+ `mcp server "${serverName}" ${reason} on ${method} ${url}: ${error.message}`,
66
+ { reason, server: serverName, action, phase: "request", statusCode: status }
67
+ )
68
+ }
69
+ }
70
+
71
+ export function createHttpMcpClient(serverName, config) {
72
+ const baseUrl = String(config.url || "").replace(/\/$/, "")
73
+ const timeoutMs = Number(config.timeout_ms || 10000)
74
+ const headers = config.headers || {}
75
+
76
+ return {
77
+ serverName,
78
+ transport: "http",
79
+ async health() {
80
+ try {
81
+ await requestJson({ serverName, method: "GET", url: `${baseUrl}/health`, timeoutMs, headers })
82
+ return { ok: true }
83
+ } catch (error) {
84
+ return { ok: false, error: error.message, reason: error.reason || "unknown" }
85
+ }
86
+ },
87
+ async listTools() {
88
+ const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/tools`, timeoutMs, headers })
89
+ return Array.isArray(out?.tools) ? out.tools : []
90
+ },
91
+ async listPrompts() {
92
+ try {
93
+ const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/prompts`, timeoutMs, headers })
94
+ return Array.isArray(out?.prompts) ? out.prompts : []
95
+ } catch {
96
+ return []
97
+ }
98
+ },
99
+ async getPrompt(name, args = {}) {
100
+ return requestJson({
101
+ serverName,
102
+ method: "POST",
103
+ url: `${baseUrl}/prompts/${encodeURIComponent(name)}`,
104
+ body: { arguments: args },
105
+ timeoutMs,
106
+ headers
107
+ })
108
+ },
109
+ async listResources() {
110
+ try {
111
+ const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/resources`, timeoutMs, headers })
112
+ return Array.isArray(out?.resources) ? out.resources : []
113
+ } catch {
114
+ return []
115
+ }
116
+ },
117
+ async listTemplates() {
118
+ try {
119
+ const out = await requestJson({ serverName, method: "GET", url: `${baseUrl}/templates`, timeoutMs, headers })
120
+ return Array.isArray(out?.templates) ? out.templates : []
121
+ } catch {
122
+ return []
123
+ }
124
+ },
125
+ async callTool(name, args = {}, signal = null) {
126
+ const result = await requestJson({
127
+ serverName,
128
+ method: "POST",
129
+ url: `${baseUrl}/tools/${encodeURIComponent(name)}`,
130
+ body: { args },
131
+ timeoutMs,
132
+ headers,
133
+ signal
134
+ })
135
+ return normalizeToolResult(result, serverName, name)
136
+ },
137
+ shutdown() {
138
+ // HTTP client is stateless — no persistent connections to clean up
139
+ }
140
+ }
141
+ }