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.
Files changed (55) hide show
  1. package/README.md +27 -0
  2. package/bin/thincoder.mjs +115 -0
  3. package/package.json +1 -1
  4. package/src/advisor.mjs +105 -0
  5. package/src/agent/dispatch.mjs +35 -0
  6. package/src/agent/setup.mjs +9 -10
  7. package/src/agent-tools/subagent.mjs +1 -1
  8. package/src/agent-tools/timer.mjs +41 -0
  9. package/src/agent-tools/verify.mjs +165 -56
  10. package/src/agent-tools.mjs +1 -0
  11. package/src/agent.mjs +128 -21
  12. package/src/auto-think.mjs +83 -0
  13. package/src/cli/make-agent.mjs +9 -0
  14. package/src/config.mjs +18 -18
  15. package/src/context.mjs +3 -1
  16. package/src/distill.mjs +19 -4
  17. package/src/embedding.mjs +3 -1
  18. package/src/git/checkpoint.mjs +2 -1
  19. package/src/git/gitmem.mjs +8 -2
  20. package/src/markdown.mjs +1 -1
  21. package/src/mcp/transport-http.mjs +11 -4
  22. package/src/memory/code-index.mjs +2 -2
  23. package/src/memory/code-sync.mjs +92 -35
  24. package/src/memory/core.mjs +10 -1
  25. package/src/memory/docs.mjs +25 -28
  26. package/src/memory/schema.mjs +16 -3
  27. package/src/prompts/coder.md +7 -4
  28. package/src/prompts/discipline.md +47 -15
  29. package/src/prompts/main.md +15 -11
  30. package/src/prompts/system.md +33 -7
  31. package/src/provider/core.mjs +142 -15
  32. package/src/provider/index.mjs +1 -1
  33. package/src/rules.mjs +53 -0
  34. package/src/session.mjs +9 -3
  35. package/src/tools/file.mjs +114 -5
  36. package/src/tools/hashline_edit.md +12 -0
  37. package/src/tools/index.mjs +6 -4
  38. package/src/tools/linter.md +13 -0
  39. package/src/tools/linter.mjs +146 -0
  40. package/src/tools/patch.mjs +7 -3
  41. package/src/tools/read.md +3 -2
  42. package/src/tools/repomap.mjs +19 -10
  43. package/src/tools/shared.mjs +7 -0
  44. package/src/tools/system.mjs +18 -4
  45. package/src/tui/agent-turn.mjs +17 -2
  46. package/src/tui/ansi.mjs +5 -0
  47. package/src/tui/cmd-advisor.mjs +68 -0
  48. package/src/tui/cmd-think.mjs +36 -10
  49. package/src/tui/index.mjs +167 -54
  50. package/src/tui/key-handler.mjs +36 -1
  51. package/src/tui/layout.mjs +6 -4
  52. package/src/tui/pickers.mjs +15 -15
  53. package/src/tui/render-frame.mjs +240 -167
  54. package/src/tui/slash-commands.mjs +3 -0
  55. 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
- }