thincoder 0.12.10 → 0.12.12
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/README.md +19 -2
- package/bin/thincoder.mjs +11 -1
- package/package.json +1 -1
- package/src/acp/bridge.mjs +229 -0
- package/src/acp/session.mjs +46 -0
- package/src/acp/transport.mjs +155 -0
- package/src/acp.mjs +335 -0
- package/src/advisor/citations.mjs +77 -0
- package/src/advisor/history.mjs +25 -6
- package/src/advisor/messages.mjs +76 -22
- package/src/advisor/run.mjs +185 -91
- package/src/advisor.mjs +209 -87
- package/src/agent/completion.mjs +9 -2
- package/src/agent/dispatch.mjs +14 -0
- package/src/agent-tools/advisor.mjs +12 -11
- package/src/agent-tools/subagent.mjs +73 -9
- package/src/agent.mjs +5 -5
- package/src/config.mjs +3 -0
- package/src/prompts/advisor-round1.md +8 -2
- package/src/prompts/advisor-round2.md +6 -4
- package/src/prompts/advisor-round3.md +6 -4
- package/src/prompts/discipline.md +1 -1
- package/src/session.mjs +19 -0
- package/src/tools/file.mjs +30 -0
- package/src/tools/insert_after.md +1 -0
- package/src/tools/patch.mjs +4 -0
- package/src/tools/shared.mjs +68 -57
- package/src/tools/system.mjs +13 -5
- package/src/tui/agent-turn.mjs +73 -121
- package/src/tui/cmd-shell.mjs +104 -0
- package/src/tui/cmd-submodel.mjs +151 -0
- package/src/tui/index.mjs +4 -3
- package/src/tui/markdown.mjs +26 -8
- package/src/tui/pickers.mjs +32 -1
- package/src/tui/render-conversation.mjs +103 -37
- package/src/tui/render.mjs +24 -5
- package/src/tui/slash-commands.mjs +10 -0
- package/src/tui/tool-summaries.mjs +113 -0
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* tool-summaries.mjs — one-line tool-result summaries for the TUI "done" lines.
|
|
3
|
+
* Extracted from agent-turn.mjs (file-size split): pure functions, no state.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/** Extract a one-line summary from tool output for the done line */
|
|
7
|
+
export function formatToolSummary(name, result) {
|
|
8
|
+
if (name === "verify") return _verifySummary(result)
|
|
9
|
+
if (name === "bash") return _bashSummary(result)
|
|
10
|
+
if (name === "advisor") return _advisorSummary(result)
|
|
11
|
+
if (name === "read" || name === "read_file") return _readSummary(result)
|
|
12
|
+
if (name === "write" || name === "write_file") return _writeSummary(result)
|
|
13
|
+
if (name === "grep" || name === "search") return _grepSummary(result)
|
|
14
|
+
if (name === "glob") return _globSummary(result)
|
|
15
|
+
// Default: first non-empty line
|
|
16
|
+
const first = result.split("\n").find((l) => l.trim())
|
|
17
|
+
return first ? `${name}: ${first.slice(0, 100)}` : null
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function _readSummary(result) {
|
|
21
|
+
const lines = result.split("\n")
|
|
22
|
+
// Look for line count in result
|
|
23
|
+
const countMatch = result.match(/(\d+) lines?/)
|
|
24
|
+
if (countMatch) return `${countMatch[1]} lines`
|
|
25
|
+
// Fallback: count actual lines
|
|
26
|
+
return `${lines.length} lines`
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function _writeSummary(result) {
|
|
30
|
+
// Extract file size or confirmation
|
|
31
|
+
if (result.includes("wrote") || result.includes("created")) {
|
|
32
|
+
const sizeMatch = result.match(/(\d+)(?:\s*(?:bytes?|chars?))/i)
|
|
33
|
+
return sizeMatch ? `wrote ${sizeMatch[1]} bytes` : "wrote file"
|
|
34
|
+
}
|
|
35
|
+
const first = result.split("\n").find((l) => l.trim())
|
|
36
|
+
return first ? first.slice(0, 80) : "wrote"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function _grepSummary(result) {
|
|
40
|
+
const lines = result.split("\n").filter((l) => l.trim())
|
|
41
|
+
const count = lines.length
|
|
42
|
+
if (count === 0) return "no matches"
|
|
43
|
+
if (count === 1) return "1 match"
|
|
44
|
+
return `${count} matches`
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function _globSummary(result) {
|
|
48
|
+
const lines = result.split("\n").filter((l) => l.trim())
|
|
49
|
+
const count = lines.length
|
|
50
|
+
if (count === 0) return "no files"
|
|
51
|
+
if (count === 1) return "1 file"
|
|
52
|
+
return `${count} files`
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* bash result format: "[stdout]:\n<out>\n\n[stderr]:\n<err>\n\n(exit code 0)".
|
|
57
|
+
* The first non-empty line is always the "[stdout]:" marker — useless as a summary.
|
|
58
|
+
* Show the LAST output line (usually the meaningful tail) plus the exit status.
|
|
59
|
+
*/
|
|
60
|
+
function _bashSummary(result) {
|
|
61
|
+
const isMarker = (l) => /^\[(stdout|stderr)\]:$/.test(l) || /^\((exit code|killed)/.test(l)
|
|
62
|
+
const lines = result.split("\n").map((l) => l.trim()).filter((l) => l && !isMarker(l))
|
|
63
|
+
const status = result.match(/\((?:exit code|killed)[^)]*\)/)?.[0]
|
|
64
|
+
const parts = []
|
|
65
|
+
if (lines.length > 0) parts.push(lines[lines.length - 1].slice(0, 100))
|
|
66
|
+
if (status) parts.push(status)
|
|
67
|
+
return parts.length > 0 ? `bash: ${parts.join(" ")}` : null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function _advisorSummary(result) {
|
|
71
|
+
const text = String(result ?? "")
|
|
72
|
+
// Error / skip messages — extract the reason after "Advisor:"
|
|
73
|
+
const errMatch = text.trimStart().match(/^Advisor:\s*(.+)/)
|
|
74
|
+
if (errMatch) return `advisor: ${errMatch[1].split(".")[0]}`
|
|
75
|
+
const critical = (text.match(/\| \d+ \|.*\| 🔴/g) || []).length
|
|
76
|
+
const advisory = (text.match(/\| \d+ \|.*\| 🟡/g) || []).length
|
|
77
|
+
const style = (text.match(/\| \d+ \|.*\| 🔵/g) || []).length
|
|
78
|
+
// Protocol: zero 🔴 rows in the review table = pass (phrase fallback for
|
|
79
|
+
// table-free summaries like "No issues found").
|
|
80
|
+
if (critical === 0 && (/\| \d+ \|/.test(text)
|
|
81
|
+
|| /no\s+🔴|all.*(?:resolved|fixed|pass)|pass(?:es|ed)?\b|no\s+(?:critical\s+)?issues?/i.test(text))) {
|
|
82
|
+
return "advisor: passed"
|
|
83
|
+
}
|
|
84
|
+
const parts = []
|
|
85
|
+
if (critical) parts.push(`${critical} critical`)
|
|
86
|
+
if (advisory) parts.push(`${advisory} advisory`)
|
|
87
|
+
if (style) parts.push(`${style} style`)
|
|
88
|
+
if (parts.length === 0) return null
|
|
89
|
+
return `advisor: ${parts.join(", ")}`
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function _verifySummary(result) {
|
|
93
|
+
const lines = result.split("\n")
|
|
94
|
+
const summary = []
|
|
95
|
+
// Changed files count
|
|
96
|
+
const changed = lines.find((l) => l.startsWith("Changed files:"))
|
|
97
|
+
if (changed) {
|
|
98
|
+
const m = changed.match(/files changed/) ? changed.replace(/^Changed files \(.*?\)/, "Changed files") : changed
|
|
99
|
+
summary.push(m)
|
|
100
|
+
}
|
|
101
|
+
// Syntax check results
|
|
102
|
+
const syntax = lines.filter((l) => l.startsWith(" ✗"))
|
|
103
|
+
if (syntax.length > 0) {
|
|
104
|
+
summary.push(`${syntax.length} syntax error(s)`)
|
|
105
|
+
}
|
|
106
|
+
// Test results
|
|
107
|
+
const testLine = lines.find((l) => l.startsWith("✓ Tests passed.") || l.startsWith("✗ Tests FAILED"))
|
|
108
|
+
if (testLine) summary.push(testLine.trim())
|
|
109
|
+
// Task list
|
|
110
|
+
const taskLine = lines.find((l) => l.startsWith("Task list:"))
|
|
111
|
+
if (taskLine) summary.push(taskLine)
|
|
112
|
+
return summary.length > 0 ? `verify: ${summary.join(" — ")}` : ""
|
|
113
|
+
}
|