iterate-plugin 2.10.0 → 2.11.0
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 +1 -1
- package/README.zh-CN.md +1 -1
- package/dist/config-loader.js +14 -3
- package/dist/config-write.js +7 -4
- package/dist/evidence.js +67 -1
- package/dist/git-scope.js +35 -6
- package/dist/meta-review.js +19 -5
- package/dist/method-scope.js +5 -1
- package/dist/review-scope.js +12 -8
- package/dist/review.js +76 -24
- package/dist/skill-prompt.js +45 -15
- package/dist/tools/checkpoint.js +10 -3
- package/dist/tools/context.js +16 -4
- package/dist/tools/decision-log.js +29 -9
- package/dist/tools/fix.js +120 -3
- package/dist/tools/prune.js +16 -9
- package/dist/tools/review.js +4 -1
- package/dist/tools/triage.js +9 -6
- package/dist/tools/validate.js +5 -2
- package/lib/client.js +152 -80
- package/lib/parse.js +26 -17
- package/package.json +1 -1
- package/src/client/index.ts +144 -56
- package/src/config-loader.ts +12 -2
- package/src/config-write.ts +6 -4
- package/src/evidence.ts +69 -1
- package/src/git-scope.ts +34 -6
- package/src/meta-review.ts +24 -10
- package/src/method-scope.ts +5 -1
- package/src/review-scope.ts +11 -7
- package/src/review.ts +82 -25
- package/src/skill-prompt.ts +45 -15
- package/src/tools/checkpoint.ts +10 -3
- package/src/tools/context.ts +14 -3
- package/src/tools/decision-log.ts +27 -10
- package/src/tools/fix.ts +114 -3
- package/src/tools/prune.ts +14 -11
- package/src/tools/review.ts +5 -2
- package/src/tools/triage.ts +9 -6
- package/src/tools/validate.ts +5 -2
- package/src/types.ts +12 -0
package/src/tools/prune.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* - Each deletion is logged to the decision log (when not dry-run).
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
|
-
import { existsSync, readdirSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
21
|
+
import { existsSync, readdirSync, renameSync, rmSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
22
22
|
import { join } from 'node:path'
|
|
23
23
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
24
24
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
@@ -134,17 +134,17 @@ export function executePrune(
|
|
|
134
134
|
errors: [] as string[],
|
|
135
135
|
}
|
|
136
136
|
|
|
137
|
-
// 1. Rewrite the decision log, keeping only recent entries.
|
|
137
|
+
// 1. Rewrite the decision log, keeping only recent entries. Atomic
|
|
138
|
+
// (temp + rename) so a crash mid-write can never truncate the log.
|
|
138
139
|
try {
|
|
139
140
|
const entries = readDecisionEntries(projectRoot)
|
|
140
141
|
const kept = entries.filter((e) => e.timestamp >= cutoff)
|
|
141
142
|
result.deletedLogEntries = entries.length - kept.length
|
|
142
143
|
if (result.deletedLogEntries > 0) {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
)
|
|
144
|
+
const logPath = join(iterateDir(projectRoot), 'decision-log.jsonl')
|
|
145
|
+
const tmpPath = `${logPath}.tmp-${Date.now()}`
|
|
146
|
+
writeFileSync(tmpPath, kept.map((e) => JSON.stringify(e)).join('\n') + '\n', 'utf-8')
|
|
147
|
+
renameSync(tmpPath, logPath)
|
|
148
148
|
}
|
|
149
149
|
} catch (err) {
|
|
150
150
|
result.errors.push(`failed to rewrite decision log: ${String(err)}`)
|
|
@@ -175,10 +175,13 @@ export function executePrune(
|
|
|
175
175
|
if (report.emptyRounds.length > 0) {
|
|
176
176
|
try {
|
|
177
177
|
let registry = readRegistry(projectRoot)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
178
|
+
const emptyRoundNos = new Set(report.emptyRounds)
|
|
179
|
+
// Drop whole empty rounds (records.length === 0) instead of only
|
|
180
|
+
// removing their records — an empty round has no records to remove, so
|
|
181
|
+
// the old loop was a no-op that still reported trimmedEmptyRounds.
|
|
182
|
+
registry = {
|
|
183
|
+
...registry,
|
|
184
|
+
rounds: registry.rounds.filter((r) => !emptyRoundNos.has(r.round) || (r.records?.length ?? 0) > 0),
|
|
182
185
|
}
|
|
183
186
|
registry = recomputeRoundCounts(registry)
|
|
184
187
|
writeFileSync(fixRegistryPath(projectRoot), JSON.stringify(registry, null, 2), 'utf-8')
|
package/src/tools/review.ts
CHANGED
|
@@ -170,9 +170,12 @@ export function registerReviewTool(ctx: { tools: { register: (def: ReturnType<ty
|
|
|
170
170
|
const rawRounds = Array.isArray(args.rounds) ? args.rounds : []
|
|
171
171
|
const rounds: ReviewRound[] = rawRounds
|
|
172
172
|
.map((r) => {
|
|
173
|
-
const rr = r as { round?: number; findings?: unknown }
|
|
173
|
+
const rr = r as { round?: number; findings?: unknown; readFiles?: unknown }
|
|
174
174
|
const findings = Array.isArray(rr?.findings) ? (rr.findings as ReviewFinding[]) : []
|
|
175
|
-
|
|
175
|
+
const readFiles = Array.isArray(rr?.readFiles)
|
|
176
|
+
? (rr.readFiles as unknown[]).filter((f): f is string => typeof f === 'string')
|
|
177
|
+
: []
|
|
178
|
+
return { round: typeof rr?.round === 'number' ? rr.round : 0, findings, readFiles }
|
|
176
179
|
})
|
|
177
180
|
.filter((r: ReviewRound) => r.round > 0)
|
|
178
181
|
|
package/src/tools/triage.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, readFileSync, writeFileSync } from 'node:fs'
|
|
1
|
+
import { copyFileSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs'
|
|
2
2
|
import { join } from 'node:path'
|
|
3
3
|
import { defineTool } from '@deepseek-ai/dsh-tools'
|
|
4
4
|
import type { JsonValue } from '@deepseek-ai/dsh-session'
|
|
@@ -231,16 +231,19 @@ function applyEntries(
|
|
|
231
231
|
try {
|
|
232
232
|
writeFileSync(configPath, yamlText, 'utf-8')
|
|
233
233
|
} catch (err) {
|
|
234
|
-
// Rollback: restore the backup
|
|
234
|
+
// Rollback: restore the backup, or REMOVE the file we just created when
|
|
235
|
+
// there was no prior config — an empty file left behind would poison all
|
|
236
|
+
// future config reads (empty YAML is not a valid mapping).
|
|
237
|
+
let rollbackError = ''
|
|
235
238
|
try {
|
|
236
239
|
if (backupPath) copyFileSync(backupPath, configPath)
|
|
237
|
-
else if (existsSync(configPath))
|
|
238
|
-
} catch {
|
|
239
|
-
|
|
240
|
+
else if (existsSync(configPath)) rmSync(configPath, { force: true })
|
|
241
|
+
} catch (rbErr) {
|
|
242
|
+
rollbackError = `; rollback also failed: ${String(rbErr)}`
|
|
240
243
|
}
|
|
241
244
|
return {
|
|
242
245
|
ok: false,
|
|
243
|
-
error: `Failed to write config: ${String(err)}`,
|
|
246
|
+
error: `Failed to write config: ${String(err)}${rollbackError}`,
|
|
244
247
|
}
|
|
245
248
|
}
|
|
246
249
|
|
package/src/tools/validate.ts
CHANGED
|
@@ -46,10 +46,13 @@ async function runCommand(
|
|
|
46
46
|
},
|
|
47
47
|
(error, stdout, stderr) => {
|
|
48
48
|
const durationMs = Math.round(performance.now() - start)
|
|
49
|
-
// error.code is the exit code when the command ran;
|
|
49
|
+
// error.code is the exit code when the command ran; when the binary
|
|
50
|
+
// cannot be spawned Node sets error.code to a STRING ('ENOENT' etc).
|
|
51
|
+
// Coerce to a number so the integer output schema is never violated.
|
|
52
|
+
const exitCode = typeof error?.code === 'number' ? error.code : (error ? 1 : 0)
|
|
50
53
|
resolve({
|
|
51
54
|
command,
|
|
52
|
-
exitCode
|
|
55
|
+
exitCode,
|
|
53
56
|
stdout: stdout ?? '',
|
|
54
57
|
stderr: stderr ?? '',
|
|
55
58
|
timedOut: error?.killed === true,
|
package/src/types.ts
CHANGED
|
@@ -76,6 +76,11 @@ export interface ReviewReport {
|
|
|
76
76
|
rounds: ReviewRound[]
|
|
77
77
|
/** Globally deduped, known-intentional-filtered, severity-sorted findings. */
|
|
78
78
|
findings: ReviewFinding[]
|
|
79
|
+
/**
|
|
80
|
+
* Every file the reviewers self-reported opening (readFiles across rounds).
|
|
81
|
+
* Consumed by the meta-review coverage gate. Optional for back-compat.
|
|
82
|
+
*/
|
|
83
|
+
readFiles?: string[]
|
|
79
84
|
convergence: {
|
|
80
85
|
totalRounds: number
|
|
81
86
|
findingsByRound: number[]
|
|
@@ -98,6 +103,13 @@ export interface ReviewReport {
|
|
|
98
103
|
export interface ReviewRound {
|
|
99
104
|
round: number
|
|
100
105
|
findings: ReviewFinding[]
|
|
106
|
+
/**
|
|
107
|
+
* Files the reviewer subagents actually opened with read_file (from their
|
|
108
|
+
* `readFiles` output). Threaded through aggregate → report → meta-review so
|
|
109
|
+
* the coverage gate can compare self-reported reads against the assigned
|
|
110
|
+
* inventory. Optional — older callers omit it.
|
|
111
|
+
*/
|
|
112
|
+
readFiles?: string[]
|
|
101
113
|
}
|
|
102
114
|
|
|
103
115
|
/** Known intentional entry (filtered out from findings) */
|