thincoder 0.7.8 → 0.8.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/README.md +32 -13
- package/bin/thincoder.js +4 -0
- package/bin/thincoder.mjs +27 -346
- package/package.json +2 -2
- package/src/agent/dispatch.mjs +98 -0
- package/src/agent/helpers.mjs +185 -0
- package/src/agent/setup.mjs +117 -0
- package/src/agent-tools/goal.mjs +71 -0
- package/src/agent-tools/plan.mjs +31 -0
- package/src/agent-tools/recent-changes.mjs +23 -0
- package/src/agent-tools/skill.mjs +46 -0
- package/src/agent-tools/subagent.mjs +113 -0
- package/src/agent-tools/task.mjs +67 -0
- package/src/agent-tools/verify.mjs +198 -0
- package/src/agent-tools.mjs +12 -0
- package/src/agent.mjs +90 -1040
- package/src/cli/distill-command.mjs +85 -0
- package/src/cli/make-agent.mjs +85 -0
- package/src/cli/memory-command.mjs +63 -0
- package/src/cli/permission.mjs +41 -0
- package/src/cli/setup-wizard.mjs +70 -0
- package/src/config.mjs +5 -8
- package/src/context.mjs +10 -13
- package/src/distill.mjs +4 -3
- package/src/embedding.mjs +4 -2
- package/src/{checkpoint.mjs → git/checkpoint.mjs} +1 -1
- package/src/mcp/helpers.mjs +37 -0
- package/src/mcp/transport-http.mjs +176 -0
- package/src/mcp/transport-stdio.mjs +84 -0
- package/src/mcp/transport-ws.mjs +87 -0
- package/src/mcp.mjs +4 -428
- package/src/memory/code-index.mjs +211 -0
- package/src/memory/code-sync.mjs +306 -0
- package/src/memory/core.mjs +277 -0
- package/src/memory/docs.mjs +262 -0
- package/src/memory/schema.mjs +426 -0
- package/src/memory.mjs +12 -1403
- package/src/provider/core.mjs +239 -0
- package/src/provider/index.mjs +6 -0
- package/src/provider/rate.mjs +104 -0
- package/src/session.mjs +18 -5
- package/src/tools/bash.mjs +144 -0
- package/src/tools/file.mjs +205 -0
- package/src/tools/git.mjs +166 -0
- package/src/tools/glob.mjs +51 -0
- package/src/tools/grep.mjs +100 -0
- package/src/tools/index.mjs +22 -0
- package/src/tools/ls.mjs +36 -0
- package/src/tools/patch.mjs +226 -0
- package/src/tools/repomap-parse.mjs +168 -0
- package/src/tools/shared.mjs +257 -0
- package/src/tools/system.mjs +336 -0
- package/src/tools/web.mjs +121 -0
- package/src/tools.mjs +2 -1194
- package/src/tui/agent-turn.mjs +254 -0
- package/src/tui/ansi.mjs +32 -0
- package/src/tui/clipboard.mjs +48 -0
- package/src/tui/cmd-auto.mjs +21 -0
- package/src/tui/cmd-clear.mjs +26 -0
- package/src/tui/cmd-config.mjs +72 -0
- package/src/tui/cmd-exit.mjs +5 -0
- package/src/tui/cmd-extract.mjs +5 -0
- package/src/tui/cmd-goal.mjs +47 -0
- package/src/tui/cmd-help.mjs +25 -0
- package/src/tui/cmd-init.mjs +91 -0
- package/src/tui/cmd-mcp.mjs +146 -0
- package/src/tui/cmd-model.mjs +7 -0
- package/src/tui/cmd-new.mjs +18 -0
- package/src/tui/cmd-plan.mjs +21 -0
- package/src/tui/cmd-reindex.mjs +44 -0
- package/src/tui/cmd-restore.mjs +39 -0
- package/src/tui/cmd-session.mjs +42 -0
- package/src/tui/cmd-skills.mjs +17 -0
- package/src/tui/cmd-think.mjs +56 -0
- package/src/tui/config-helpers.mjs +34 -0
- package/src/tui/distill-cmd.mjs +45 -0
- package/src/tui/index.mjs +330 -0
- package/src/tui/interaction.mjs +79 -0
- package/src/tui/key-handler.mjs +267 -0
- package/src/tui/layout.mjs +115 -0
- package/src/tui/pickers.mjs +279 -0
- package/src/tui/render-frame.mjs +304 -0
- package/src/tui/render.mjs +205 -0
- package/src/tui/slash-commands.mjs +138 -0
- package/src/tui/startup.mjs +113 -0
- package/src/tui/wizard.mjs +168 -0
- package/src/tui-render.mjs +4 -0
- package/src/tui.mjs +3 -2566
- package/src/provider.mjs +0 -383
- /package/src/{gitmem.mjs → git/gitmem.mjs} +0 -0
- /package/src/{coder-overlay.md → prompts/coder.md} +0 -0
- /package/src/{discipline-rules.md → prompts/discipline.md} +0 -0
- /package/src/{explore-overlay.md → prompts/explore.md} +0 -0
- /package/src/{main-overlay.md → prompts/main.md} +0 -0
- /package/src/{plan-overlay.md → prompts/plan.md} +0 -0
- /package/src/{SYSTEM_PROMPT.md → prompts/system.md} +0 -0
- /package/src/{repomap.mjs → tools/repomap.mjs} +0 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { repairHistory, listWorkDir } from "../agent.mjs"
|
|
2
|
+
import { execSync, spawn } from "node:child_process"
|
|
3
|
+
import { readFileSync, existsSync } from "node:fs"
|
|
4
|
+
import { join } from "node:path"
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* verify 工具:完成前的自检。调用时会:
|
|
8
|
+
* 1. git diff --stat — 变更文件列表
|
|
9
|
+
* 2. node --check — 语法检查所有变更的 .mjs/.js 文件
|
|
10
|
+
* 3. npm test — 仅在 full=true 时运行项目测试
|
|
11
|
+
* 4. task 列表 + 自检清单
|
|
12
|
+
* 默认只做语法检查(快),full=true 时才跑全量测试。Agent 不应该在 verify 通过前说"完成"。修复-验证循环最多 MAX_VERIFY_RETRIES 轮。
|
|
13
|
+
*/
|
|
14
|
+
export const verifyTool = {
|
|
15
|
+
name: "verify",
|
|
16
|
+
description:
|
|
17
|
+
"Run a pre-completion self-check. By default runs syntax checks on changed files, shows git diff and task list, and displays a self-review checklist. Set full=true to also run the project's full test suite (npm test). Call this BEFORE declaring any coding task complete — do not say 'done' until verify passes.",
|
|
18
|
+
parameters: {
|
|
19
|
+
type: "object",
|
|
20
|
+
properties: {
|
|
21
|
+
full: { type: "boolean", description: "Also run the full test suite (npm test). Default false — only run when completing a task or the user asks." },
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
readonly: true,
|
|
25
|
+
async execute(args, ctx) {
|
|
26
|
+
const cwd = ctx.agent.cwd
|
|
27
|
+
const lines = []
|
|
28
|
+
lines.push("=== VERIFICATION REPORT ===")
|
|
29
|
+
lines.push("")
|
|
30
|
+
|
|
31
|
+
// 1. Git diff — 找出变更文件
|
|
32
|
+
let changedFiles = []
|
|
33
|
+
try {
|
|
34
|
+
const diff = execSync("git diff --stat", { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 })
|
|
35
|
+
if (diff.trim()) {
|
|
36
|
+
lines.push("Changed files (git diff --stat):")
|
|
37
|
+
lines.push(diff.trim())
|
|
38
|
+
// 提取变更文件路径
|
|
39
|
+
const nameOnly = execSync("git diff --name-only", { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "ignore"], timeout: 5000 })
|
|
40
|
+
changedFiles = nameOnly.trim().split("\n").filter(Boolean)
|
|
41
|
+
} else {
|
|
42
|
+
lines.push("Changed files: (none — no uncommitted changes)")
|
|
43
|
+
}
|
|
44
|
+
} catch {
|
|
45
|
+
lines.push("Changed files: (not a git repo or git unavailable)")
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 2. 语法检查:对所有变更的 .mjs/.js 跑 node --check(跳过已删除的文件)
|
|
49
|
+
let syntaxFailed = false
|
|
50
|
+
const jsFiles = changedFiles.filter((f) => /\.(m?js)$/i.test(f))
|
|
51
|
+
if (jsFiles.length > 0) {
|
|
52
|
+
lines.push("")
|
|
53
|
+
lines.push("Syntax check (node --check):")
|
|
54
|
+
for (const f of jsFiles) {
|
|
55
|
+
const abs = join(cwd, f)
|
|
56
|
+
if (!existsSync(abs)) continue // 已删除的文件跳过
|
|
57
|
+
try {
|
|
58
|
+
execSync(`node --check "${f}"`, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"], timeout: 10000 })
|
|
59
|
+
lines.push(` ✓ ${f}`)
|
|
60
|
+
} catch (e) {
|
|
61
|
+
syntaxFailed = true
|
|
62
|
+
const errMsg = (e.stderr || e.stdout || e.message || "").toString().split("\n").slice(0, 3).join("\n")
|
|
63
|
+
lines.push(` ✗ ${f} — syntax error`)
|
|
64
|
+
lines.push(` ${errMsg.replace(/\n/g, "\n ")}`)
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (!syntaxFailed) lines.push(" All syntax checks passed.")
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 3. 运行项目测试(仅 full=true 时)
|
|
71
|
+
if (args.full) {
|
|
72
|
+
try {
|
|
73
|
+
const pkgPath = join(cwd, "package.json")
|
|
74
|
+
if (existsSync(pkgPath)) {
|
|
75
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"))
|
|
76
|
+
const testCmd = pkg.scripts?.test
|
|
77
|
+
if (testCmd) {
|
|
78
|
+
lines.push("")
|
|
79
|
+
lines.push(`Tests (${testCmd}):`)
|
|
80
|
+
try {
|
|
81
|
+
const result = await runTestSuite(cwd, ctx)
|
|
82
|
+
const tail = result.stdout.split("\n").slice(-8).join("\n")
|
|
83
|
+
lines.push(tail || "(tests completed)")
|
|
84
|
+
lines.push("")
|
|
85
|
+
lines.push("✓ Tests passed.")
|
|
86
|
+
ctx.agent._verifyPassed = !syntaxFailed // 语法挂了即使测试侥幸过也不算通过
|
|
87
|
+
} catch (e) {
|
|
88
|
+
const output = e.stdout ? (e.stdout + (e.stderr ? "\n" + e.stderr : "")) : e.message
|
|
89
|
+
const tail = output.split("\n").slice(-15).join("\n")
|
|
90
|
+
lines.push(tail || "(no output)")
|
|
91
|
+
lines.push("")
|
|
92
|
+
lines.push("✗ Tests FAILED. Review the output above, fix the issues, then run verify again.")
|
|
93
|
+
ctx.agent._verifyPassed = false
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
lines.push("")
|
|
97
|
+
lines.push("Tests: no test script in package.json — skipped.")
|
|
98
|
+
ctx.agent._verifyPassed = !syntaxFailed
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
} catch {
|
|
102
|
+
lines.push("Tests: (unable to run — no package.json or npm unavailable)")
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
// 快速模式:跳过测试,但提示可以跑完整校验
|
|
106
|
+
const pkgPath = join(cwd, "package.json")
|
|
107
|
+
if (existsSync(pkgPath)) {
|
|
108
|
+
try {
|
|
109
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf8"))
|
|
110
|
+
if (pkg.scripts?.test) {
|
|
111
|
+
lines.push("")
|
|
112
|
+
lines.push("Tests: skipped (default quick mode). Run verify with full=true or npm test to run the full suite.")
|
|
113
|
+
}
|
|
114
|
+
} catch { /* ignore */ }
|
|
115
|
+
}
|
|
116
|
+
ctx.agent._verifyPassed = !syntaxFailed // quick 模式:语法失败不能算通过
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// 4. Task 列表
|
|
120
|
+
lines.push("")
|
|
121
|
+
if (ctx.agent.tasks.length === 0) {
|
|
122
|
+
lines.push("Task list: (no tasks tracked)")
|
|
123
|
+
} else {
|
|
124
|
+
const done = ctx.agent.tasks.filter((t) => t.status === "done").length
|
|
125
|
+
const total = ctx.agent.tasks.length
|
|
126
|
+
const open = ctx.agent.tasks.filter((t) => t.status !== "done")
|
|
127
|
+
lines.push(`Task list: ${done}/${total} done`)
|
|
128
|
+
for (const t of ctx.agent.tasks) {
|
|
129
|
+
const mark = t.status === "done" ? "✓" : t.status === "in_progress" ? "▶" : "○"
|
|
130
|
+
lines.push(` ${mark} [${t.status}] ${t.title}`)
|
|
131
|
+
}
|
|
132
|
+
if (open.length > 0) {
|
|
133
|
+
lines.push("")
|
|
134
|
+
lines.push(`WARNING: ${open.length} task(s) still open. Complete them or explain why they can be left undone.`)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// 5. Checklist
|
|
139
|
+
lines.push("")
|
|
140
|
+
lines.push("Self-review checklist:")
|
|
141
|
+
lines.push("- [ ] Did I run the project's tests and do they pass?")
|
|
142
|
+
lines.push("- [ ] Did I read every file I changed to catch leftover debug code or stale comments?")
|
|
143
|
+
lines.push("- [ ] Do comments and docstrings match what the code actually does?")
|
|
144
|
+
lines.push("- [ ] Did I remove placeholder code, TODO stubs, or commented-out experiment blocks?")
|
|
145
|
+
lines.push("- [ ] If I used a subagent, did I verify its report against the actual files it touched?")
|
|
146
|
+
lines.push("- [ ] Are all task items genuinely done (not just marked done to finish early)?")
|
|
147
|
+
|
|
148
|
+
return lines.join("\n")
|
|
149
|
+
},
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* 用 spawn 运行 npm test,无 maxBuffer 限制。
|
|
154
|
+
* 测试输出通过 ctx.callbacks.onToolOutput 流式透传(TUI 可实时显示进度)。
|
|
155
|
+
* 成功返回 { stdout, stderr };非零退出码抛错(带 stdout/stderr 供调用方提取 tail)。
|
|
156
|
+
*/
|
|
157
|
+
function runTestSuite(cwd, ctx) {
|
|
158
|
+
return new Promise((resolve, reject) => {
|
|
159
|
+
const child = spawn("npm", ["test"], {
|
|
160
|
+
cwd, shell: true, stdio: ["ignore", "pipe", "pipe"],
|
|
161
|
+
env: { ...process.env, FORCE_COLOR: "0" },
|
|
162
|
+
})
|
|
163
|
+
let stdout = ""
|
|
164
|
+
let stderr = ""
|
|
165
|
+
child.stdout.on("data", (d) => {
|
|
166
|
+
const s = d.toString()
|
|
167
|
+
stdout += s
|
|
168
|
+
ctx.callbacks?.onToolOutput?.("verify", s)
|
|
169
|
+
})
|
|
170
|
+
child.stderr.on("data", (d) => {
|
|
171
|
+
const s = d.toString()
|
|
172
|
+
stderr += s
|
|
173
|
+
ctx.callbacks?.onToolOutput?.("verify", s)
|
|
174
|
+
})
|
|
175
|
+
const timer = setTimeout(() => {
|
|
176
|
+
child.kill("SIGKILL")
|
|
177
|
+
const err = new Error("Tests timed out after 120s")
|
|
178
|
+
err.stdout = stdout
|
|
179
|
+
err.stderr = stderr
|
|
180
|
+
reject(err)
|
|
181
|
+
}, 120000)
|
|
182
|
+
child.on("error", (e) => {
|
|
183
|
+
clearTimeout(timer)
|
|
184
|
+
reject(e)
|
|
185
|
+
})
|
|
186
|
+
child.on("close", (code) => {
|
|
187
|
+
clearTimeout(timer)
|
|
188
|
+
if (code === 0) resolve({ stdout, stderr })
|
|
189
|
+
else {
|
|
190
|
+
const err = new Error(`Tests exited with code ${code}`)
|
|
191
|
+
err.stdout = stdout
|
|
192
|
+
err.stderr = stderr
|
|
193
|
+
reject(err)
|
|
194
|
+
}
|
|
195
|
+
})
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* agent-tools.mjs — 自律工具索引
|
|
3
|
+
* 从 agent.mjs 通过动态 import 加载以避免 ESM 循环依赖。
|
|
4
|
+
* 各工具实现在 agent-tools/ 子目录中。
|
|
5
|
+
*/
|
|
6
|
+
export { planTool } from "./agent-tools/plan.mjs"
|
|
7
|
+
export { subagentTool } from "./agent-tools/subagent.mjs"
|
|
8
|
+
export { taskTool } from "./agent-tools/task.mjs"
|
|
9
|
+
export { skillTool } from "./agent-tools/skill.mjs"
|
|
10
|
+
export { goalTool } from "./agent-tools/goal.mjs"
|
|
11
|
+
export { verifyTool } from "./agent-tools/verify.mjs"
|
|
12
|
+
export { recentChangesTool } from "./agent-tools/recent-changes.mjs"
|