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.
@@ -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
+ }