thincoder 0.8.11 → 0.8.13
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 +27 -0
- package/bin/thincoder.mjs +115 -0
- package/package.json +1 -1
- package/src/advisor.mjs +105 -0
- package/src/agent/dispatch.mjs +35 -0
- package/src/agent/setup.mjs +9 -10
- package/src/agent-tools/subagent.mjs +1 -1
- package/src/agent-tools/timer.mjs +41 -0
- package/src/agent-tools/verify.mjs +165 -56
- package/src/agent-tools.mjs +1 -0
- package/src/agent.mjs +128 -21
- package/src/auto-think.mjs +83 -0
- package/src/cli/make-agent.mjs +9 -0
- package/src/config.mjs +18 -18
- package/src/context.mjs +3 -1
- package/src/distill.mjs +19 -4
- package/src/embedding.mjs +3 -1
- package/src/git/checkpoint.mjs +2 -1
- package/src/git/gitmem.mjs +8 -2
- package/src/markdown.mjs +1 -1
- package/src/mcp/transport-http.mjs +11 -4
- package/src/memory/code-index.mjs +2 -2
- package/src/memory/code-sync.mjs +92 -35
- package/src/memory/core.mjs +10 -1
- package/src/memory/docs.mjs +25 -28
- package/src/memory/schema.mjs +16 -3
- package/src/prompts/coder.md +7 -4
- package/src/prompts/discipline.md +47 -15
- package/src/prompts/main.md +15 -11
- package/src/prompts/system.md +33 -7
- package/src/provider/core.mjs +142 -15
- package/src/provider/index.mjs +1 -1
- package/src/rules.mjs +53 -0
- package/src/session.mjs +9 -3
- package/src/tools/file.mjs +114 -5
- package/src/tools/hashline_edit.md +12 -0
- package/src/tools/index.mjs +6 -4
- package/src/tools/linter.md +13 -0
- package/src/tools/linter.mjs +146 -0
- package/src/tools/patch.mjs +7 -3
- package/src/tools/read.md +3 -2
- package/src/tools/repomap.mjs +19 -10
- package/src/tools/shared.mjs +7 -0
- package/src/tools/system.mjs +18 -4
- package/src/tui/agent-turn.mjs +17 -2
- package/src/tui/ansi.mjs +5 -0
- package/src/tui/cmd-advisor.mjs +68 -0
- package/src/tui/cmd-think.mjs +36 -10
- package/src/tui/index.mjs +167 -54
- package/src/tui/key-handler.mjs +36 -1
- package/src/tui/layout.mjs +6 -4
- package/src/tui/pickers.mjs +15 -15
- package/src/tui/render-frame.mjs +240 -167
- package/src/tui/slash-commands.mjs +3 -0
- package/src/tools/repomap-parse.mjs +0 -168
|
@@ -1,168 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* repomap-parse.mjs — repo dependency graph parser (zero dependencies, pure regex)
|
|
3
|
-
* Gets known file list from code_chunks, parses each file's import/export relationships in real time,
|
|
4
|
-
* builds forward dependency graph + reverse reference graph. Shared by repomap.mjs's buildSummary / buildOutline.
|
|
5
|
-
*/
|
|
6
|
-
import { readFileSync, existsSync } from "node:fs"
|
|
7
|
-
import { join } from "node:path"
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Scan all files, build forward dependency graph + reverse reference graph.
|
|
11
|
-
* Returns { deps, importers, fileCount } shared by buildOutline / buildSummary.
|
|
12
|
-
*/
|
|
13
|
-
export function buildDepGraph(db, cwd) {
|
|
14
|
-
const allFiles = db.prepare(`SELECT DISTINCT path FROM code_chunks ORDER BY path`).all().map((r) => r.path)
|
|
15
|
-
if (allFiles.length === 0) return null
|
|
16
|
-
|
|
17
|
-
const deps = new Map() // path → { imports: Set, exports: Set, size: number, dir: string }
|
|
18
|
-
const importers = new Map() // importee → Set<importer>
|
|
19
|
-
|
|
20
|
-
for (const rel of allFiles) {
|
|
21
|
-
const abs = join(cwd, ...rel.split("/"))
|
|
22
|
-
if (!existsSync(abs)) continue
|
|
23
|
-
const text = readFileSync(abs, "utf8")
|
|
24
|
-
const lines = text.split("\n")
|
|
25
|
-
const ext = rel.slice(rel.lastIndexOf(".")).toLowerCase()
|
|
26
|
-
|
|
27
|
-
let imports, exports
|
|
28
|
-
if (ext === ".py") {
|
|
29
|
-
const py = parsePyOutline(lines)
|
|
30
|
-
imports = py.imports
|
|
31
|
-
exports = py.symbols
|
|
32
|
-
} else {
|
|
33
|
-
imports = parseImports(lines, ext)
|
|
34
|
-
exports = parseExports(lines, ext)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// Resolve import paths to relative paths (handle ./ ../)
|
|
38
|
-
const resolved = []
|
|
39
|
-
for (let imp of imports) {
|
|
40
|
-
if (imp.startsWith("./")) imp = imp.slice(2)
|
|
41
|
-
const dir = rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : ""
|
|
42
|
-
const parts = imp.split("/")
|
|
43
|
-
if (parts[0] === "..") {
|
|
44
|
-
const up = dir.split("/").filter(Boolean)
|
|
45
|
-
let i = 0
|
|
46
|
-
while (parts[i] === ".." && up.length > 0) { up.pop(); i++ }
|
|
47
|
-
resolved.push([...up, ...parts.slice(i)].join("/"))
|
|
48
|
-
} else {
|
|
49
|
-
resolved.push(dir ? `${dir}/${imp}` : imp)
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const dir = rel.includes("/") ? rel.slice(0, rel.lastIndexOf("/")) : "."
|
|
54
|
-
deps.set(rel, { imports: new Set(resolved), exports: new Set(exports), size: Math.floor(text.length / 1024), dir })
|
|
55
|
-
|
|
56
|
-
for (const r of resolved) {
|
|
57
|
-
if (!importers.has(r)) importers.set(r, new Set())
|
|
58
|
-
importers.get(r).add(rel)
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
return { deps, importers, fileCount: allFiles.length }
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
// ---------------------------------------------------------- internal implementation
|
|
66
|
-
|
|
67
|
-
function normalizeExt(p) {
|
|
68
|
-
return p.replace(/\.(m?js|jsx|tsx?)$/i, "")
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/** Extract JS/TS file import paths (normalize by stripping .ts/.js/.mjs suffixes) */
|
|
72
|
-
function parseImports(lines, ext) {
|
|
73
|
-
const imports = []
|
|
74
|
-
const text = lines.join("\n")
|
|
75
|
-
// standard import
|
|
76
|
-
const re = /import\s+(?:{[^}]*}|\*\s+as\s+\w+|\w+\s*,?\s*(?:{[^}]*})?)\s*from\s*['"]([^'"]+)['"]|import\s+['"]([^'"]+)['"]/g
|
|
77
|
-
let m
|
|
78
|
-
while ((m = re.exec(text))) {
|
|
79
|
-
const raw = m[1] || m[2]
|
|
80
|
-
if (!raw || raw.startsWith("node:") || !raw.startsWith(".")) continue
|
|
81
|
-
imports.push(normalizeExt(raw))
|
|
82
|
-
}
|
|
83
|
-
// re-export: export { x } from './module'
|
|
84
|
-
const reExportRe = /export\s*\{[^}]*\}\s*from\s*['"]([^'"]+)['"]/g
|
|
85
|
-
while ((m = reExportRe.exec(text))) {
|
|
86
|
-
const raw = m[1]
|
|
87
|
-
if (!raw || raw.startsWith("node:") || !raw.startsWith(".")) continue
|
|
88
|
-
imports.push(normalizeExt(raw))
|
|
89
|
-
}
|
|
90
|
-
return [...new Set(imports)]
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/** Extract JS/TS file export symbols */
|
|
94
|
-
function parseExports(lines, ext) {
|
|
95
|
-
const exports = []
|
|
96
|
-
const text = lines.join("\n")
|
|
97
|
-
// export function/class/const/let/var name
|
|
98
|
-
const namedRe = /export\s+(?:async\s+)?(?:function\s+(\w+)|class\s+(\w+)|(?:const|let|var)\s+(\w+))/g
|
|
99
|
-
let m
|
|
100
|
-
while ((m = namedRe.exec(text))) {
|
|
101
|
-
exports.push(m[1] || m[2] || m[3])
|
|
102
|
-
}
|
|
103
|
-
// export default function/class name / export default expression
|
|
104
|
-
const defaultRe = /export\s+default\s+(?:(?:async\s+)?(?:function\s+(\w+)|class\s+(\w+))|(\w+))/g
|
|
105
|
-
while ((m = defaultRe.exec(text))) {
|
|
106
|
-
const name = m[1] || m[2] || m[3]
|
|
107
|
-
if (name) exports.push(name)
|
|
108
|
-
else if (!exports.some((e) => e === "default")) exports.push("default")
|
|
109
|
-
}
|
|
110
|
-
// export { a, b as c } — prefer the "as" alias as the exported name
|
|
111
|
-
const braceRe = /export\s*\{([^}]+)\}/g
|
|
112
|
-
while ((m = braceRe.exec(text))) {
|
|
113
|
-
for (const name of m[1].split(",")) {
|
|
114
|
-
const parts = name.trim().split(/\s+/)
|
|
115
|
-
// "a as b" → b (exported name), "a" → a
|
|
116
|
-
const exported = parts.length >= 3 ? parts[2] : parts[0]
|
|
117
|
-
if (exported) exports.push(exported)
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// export const { a, b } = ... (destructured export)
|
|
121
|
-
const destructRe = /export\s+(?:const|let|var)\s*\{([^}]+)\}\s*=/g
|
|
122
|
-
while ((m = destructRe.exec(text))) {
|
|
123
|
-
for (const name of m[1].split(",")) {
|
|
124
|
-
const parts = name.trim().split(/\s*:\s*/)
|
|
125
|
-
const n = parts[0].trim()
|
|
126
|
-
if (n) exports.push(n)
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return [...new Set(exports)]
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/** Extract Python imports and top-level def/class */
|
|
133
|
-
function parsePyOutline(lines) {
|
|
134
|
-
const imports = []
|
|
135
|
-
const symbols = []
|
|
136
|
-
for (const line of lines) {
|
|
137
|
-
const fromRe = line.match(/^from\s+(\S+)\s+import\s+(.+)/)
|
|
138
|
-
if (fromRe) {
|
|
139
|
-
const rel = pyRelPath(fromRe[1])
|
|
140
|
-
if (rel) imports.push(rel)
|
|
141
|
-
continue
|
|
142
|
-
}
|
|
143
|
-
const impRe = line.match(/^import\s+(.+)/)
|
|
144
|
-
if (impRe) {
|
|
145
|
-
for (const mod of impRe[1].split(",")) {
|
|
146
|
-
const rel = pyRelPath(mod.trim().split(/\s+/)[0])
|
|
147
|
-
if (rel) imports.push(rel)
|
|
148
|
-
}
|
|
149
|
-
continue
|
|
150
|
-
}
|
|
151
|
-
const defRe = line.match(/^(?:async\s+)?(?:def|class)\s+(\w+)/)
|
|
152
|
-
if (defRe) symbols.push(defRe[1])
|
|
153
|
-
}
|
|
154
|
-
return { imports: [...new Set(imports)], symbols: [...new Set(symbols)] }
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
/**
|
|
158
|
-
* Python relative import → relative file path:
|
|
159
|
-
* Leading n dots mean go up n-1 levels ("." = current package), module dots become path separators.
|
|
160
|
-
* Non-relative imports (not starting with .) or bare package imports ("from . import x") return null.
|
|
161
|
-
*/
|
|
162
|
-
function pyRelPath(mod) {
|
|
163
|
-
if (!mod?.startsWith(".")) return null
|
|
164
|
-
const dots = mod.match(/^\.+/)[0].length
|
|
165
|
-
const rest = mod.slice(dots).replaceAll(".", "/")
|
|
166
|
-
if (!rest) return null
|
|
167
|
-
return normalizeExt("../".repeat(dots - 1) + rest)
|
|
168
|
-
}
|