@yolk_vat-y/dsh-project-memory 0.5.5 → 0.5.7
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/CHANGELOG.md +71 -2
- package/README.md +27 -4
- package/README.zh-CN.md +26 -3
- package/package.json +4 -2
- package/src/audit.js +87 -0
- package/src/auto-inject.js +383 -197
- package/src/chunker.js +14 -6
- package/src/commands/insight-actions.js +18 -14
- package/src/commands/invocation.js +36 -0
- package/src/commands/task-actions.js +24 -39
- package/src/commands/tasks.js +18 -13
- package/src/enhancer.js +11 -2
- package/src/index-pipeline.js +119 -0
- package/src/insight-store.js +78 -29
- package/src/lazy.js +15 -46
- package/src/link.js +10 -1
- package/src/ops.js +210 -0
- package/src/parsers/pdfjs-parser.js +2 -4
- package/src/project-profile.js +12 -5
- package/src/readiness.js +305 -37
- package/src/recall.js +28 -8
- package/src/setup/taskbridge.js +11 -9
- package/src/store.js +56 -17
- package/src/symbols.js +58 -19
- package/src/tools/forget.js +2 -1
- package/src/tools/index-doc.js +4 -1
- package/src/tools/index-repo.js +33 -86
- package/src/tools/lesson-tools.js +31 -8
- package/src/tools/query-memory.js +17 -3
- package/src/tools/remember.js +3 -1
- package/src/tools/task-tools.js +4 -1
- package/src/tools/watch-repo.js +9 -5
- package/src/util/fs.js +14 -0
- package/src/util/session-cache.js +72 -0
- package/src/watch.js +39 -81
package/src/watch.js
CHANGED
|
@@ -1,10 +1,7 @@
|
|
|
1
1
|
import { existsSync, statSync } from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { docEntriesNeedBackfill } from './doc-index.js'
|
|
6
|
-
import { scanSymbols } from './symbols.js'
|
|
7
|
-
import { linkEntries } from './link.js'
|
|
3
|
+
import { isUnwatchableRoot, memoryRootFor, relativePath, storeKey, walkDir } from './util/fs.js'
|
|
4
|
+
import { OVERSIZE, UNCHANGED, commitFileUpdates, fileKind, planFileIndex, toFileUpdate } from './index-pipeline.js'
|
|
8
5
|
import { ProjectMemoryStore } from './store.js'
|
|
9
6
|
import { onFileChanged, isTypeScriptFile } from './enhancer.js'
|
|
10
7
|
|
|
@@ -94,18 +91,16 @@ export class WatchManager {
|
|
|
94
91
|
}
|
|
95
92
|
|
|
96
93
|
async pollRoot(root, state) {
|
|
97
|
-
const files = walkDir(root)
|
|
98
94
|
const seen = new Set()
|
|
99
|
-
|
|
95
|
+
const updates = []
|
|
96
|
+
// rel → 本轮采集的 mtime:size。提交成功后才落进快照;CAS 失败的条目留在原地等下一轮。
|
|
97
|
+
const signatures = new Map()
|
|
100
98
|
|
|
101
|
-
|
|
102
|
-
const fileUpdates = []
|
|
103
|
-
|
|
104
|
-
for (const filePath of files) {
|
|
99
|
+
for (const filePath of walkDir(root)) {
|
|
105
100
|
const rel = storeKey(relativePath(root, filePath))
|
|
106
101
|
seen.add(rel)
|
|
107
|
-
const
|
|
108
|
-
if (!
|
|
102
|
+
const kind = fileKind(path.extname(filePath).toLowerCase())
|
|
103
|
+
if (!kind) continue
|
|
109
104
|
|
|
110
105
|
let stats
|
|
111
106
|
try {
|
|
@@ -116,86 +111,49 @@ export class WatchManager {
|
|
|
116
111
|
const sig = `${stats.mtimeMs}:${stats.size}`
|
|
117
112
|
if (state.snapshot[rel] === sig) continue
|
|
118
113
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// 单次读盘:同一 buffer 供哈希与正文使用
|
|
124
|
-
const { hash, buffer } = readFileForIndex(filePath)
|
|
125
|
-
const existing = state.store.fileRecord(rel)
|
|
126
|
-
// 旧 store 的 doc 条目缺 terms → 一次性回填(即使哈希未变)
|
|
127
|
-
const needsBackfill = isSupportedDoc(ext) && docEntriesNeedBackfill(state.store.entries[rel])
|
|
128
|
-
if (existing && existing.sha256 === hash && !needsBackfill) continue
|
|
129
|
-
|
|
114
|
+
const record = state.store.fileRecord(rel)
|
|
115
|
+
let plan
|
|
130
116
|
try {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
141
|
-
if (entries === null) {
|
|
142
|
-
// Dump file - update snapshot so we don't re-hash next poll, but don't index
|
|
143
|
-
fileUpdates.push({ rel, expectedHash: state.store.fileRecord(rel)?.sha256, deleted: true, _sig: sig })
|
|
144
|
-
changed++
|
|
145
|
-
state.failures?.delete(rel)
|
|
146
|
-
continue
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
fileUpdates.push({ rel, expectedHash: state.store.fileRecord(rel)?.sha256, hash, size: stats.size, entries, type: isSupportedCode(ext) ? 'code' : 'doc', _sig: sig })
|
|
150
|
-
changed++
|
|
151
|
-
state.failures?.delete(rel)
|
|
117
|
+
plan = await planFileIndex({
|
|
118
|
+
rel,
|
|
119
|
+
filePath,
|
|
120
|
+
kind,
|
|
121
|
+
config: this.config,
|
|
122
|
+
record,
|
|
123
|
+
existingEntries: state.store.entries[rel],
|
|
124
|
+
size: stats.size,
|
|
125
|
+
})
|
|
152
126
|
} catch (err) {
|
|
153
|
-
//
|
|
154
|
-
delete state.snapshot[rel]
|
|
155
|
-
// 同一个文件的同一个错误只上报一次:坏文件每轮都会重试,逐轮 console.error 会刷屏
|
|
156
|
-
if (!state.failures) state.failures = new Map()
|
|
127
|
+
// 坏文件每轮都会重试:同一个文件的同一个错误只上报一次,否则逐轮 console.error 刷屏。
|
|
157
128
|
if (state.failures.get(rel) !== err.message) {
|
|
158
129
|
state.failures.set(rel, err.message)
|
|
159
130
|
console.error(`[dsh-project-memory] re-index failed for ${rel}: ${err.message}`)
|
|
160
131
|
}
|
|
161
132
|
continue
|
|
162
133
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (result.skipped) {
|
|
170
|
-
// CAS failed - file was modified concurrently, rollback snapshot to retry next poll
|
|
171
|
-
delete state.snapshot[update.rel]
|
|
172
|
-
// Mark this update to skip snapshot update after commit
|
|
173
|
-
update._skipSnapshot = true
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
// Remove deleted files
|
|
178
|
-
for (const rel of Object.keys(s.files)) {
|
|
179
|
-
if (!seen.has(rel)) {
|
|
180
|
-
s.removeFile(rel)
|
|
181
|
-
}
|
|
134
|
+
state.failures.delete(rel)
|
|
135
|
+
// 内容未变 / 体积超限也要落快照:否则 mtime:size 快路径永远命中不了,
|
|
136
|
+
// 每次 poll 都要把整个仓库重读一遍再哈希(watch 默认开、15s 一轮)。
|
|
137
|
+
if (plan.key === UNCHANGED || plan.key === OVERSIZE) {
|
|
138
|
+
state.snapshot[rel] = sig
|
|
139
|
+
continue
|
|
182
140
|
}
|
|
183
141
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
})
|
|
142
|
+
signatures.set(rel, sig)
|
|
143
|
+
updates.push(toFileUpdate(rel, plan, record))
|
|
144
|
+
}
|
|
188
145
|
|
|
189
|
-
//
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
146
|
+
// 单事务写盘;CAS 失败的条目本轮不落快照,下一轮自然重试。
|
|
147
|
+
const { stale } = commitFileUpdates(state.store, { updates, unseen: seen, link: updates.length > 0 })
|
|
148
|
+
const failed = new Set(stale)
|
|
149
|
+
for (const update of updates) {
|
|
150
|
+
if (failed.has(update.rel)) continue
|
|
151
|
+
state.snapshot[update.rel] = signatures.get(update.rel)
|
|
152
|
+
if (update.type !== 'code') continue
|
|
153
|
+
const filePath = path.join(root, update.rel)
|
|
154
|
+
if (isTypeScriptFile(filePath)) {
|
|
155
|
+
onFileChanged(state.store, update.rel, filePath, this.config, root)
|
|
197
156
|
}
|
|
198
|
-
state.snapshot[update.rel] = update._sig
|
|
199
157
|
}
|
|
200
158
|
}
|
|
201
159
|
}
|