iterate-plugin 2.7.1 → 2.7.3
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 +8 -0
- package/README.zh-CN.md +8 -0
- package/dist/index.js +6 -4
- package/dist/review.js +6 -1
- package/dist/skill-prompt.js +6 -2
- package/dist/tools/decision-log.js +41 -6
- package/dist/tools/review.js +6 -0
- package/lib/client.js +1490 -695
- package/lib/parse.js +137 -1
- package/package.json +6 -2
- package/src/client/index.ts +1218 -0
- package/src/index.ts +6 -4
- package/src/review.ts +9 -1
- package/src/skill-prompt.ts +6 -2
- package/src/tools/decision-log.ts +43 -6
- package/src/tools/review.ts +7 -0
- package/src/types.ts +3 -0
package/lib/parse.js
CHANGED
|
@@ -160,6 +160,139 @@ export function scanSessionForReport(session) {
|
|
|
160
160
|
return null
|
|
161
161
|
}
|
|
162
162
|
|
|
163
|
+
// ─── Run-summary / meta-review verdict detection ─────────────────────────────
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Check whether `obj` is an iterate dry-run run-summary object (the structured
|
|
167
|
+
* object returned by the workflow at the end of a dry-run). It wraps the
|
|
168
|
+
* ReviewReport and carries the meta-review verdict:
|
|
169
|
+
* { mode, goal, rounds, converged, ..., report, metaReview, finalReport }
|
|
170
|
+
* The discriminator is `finalReport.verdict`, which only the meta-review
|
|
171
|
+
* closing step produces ("approved" | "needs_revision"). This shape is distinct
|
|
172
|
+
* from a ReviewReport (which has `convergence`/`findings`/`rounds`), so it never
|
|
173
|
+
* collides with `isReviewReport`.
|
|
174
|
+
*
|
|
175
|
+
* @param {unknown} obj
|
|
176
|
+
* @returns {obj is Record<string, unknown>}
|
|
177
|
+
*/
|
|
178
|
+
export function isRunSummary(obj) {
|
|
179
|
+
if (!obj || typeof obj !== 'object') return false
|
|
180
|
+
const o = /** @type {Record<string, unknown>} */ (obj)
|
|
181
|
+
const final = o.finalReport
|
|
182
|
+
return !!final &&
|
|
183
|
+
typeof final === 'object' &&
|
|
184
|
+
(final.verdict === 'approved' || final.verdict === 'needs_revision')
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Deep-scan an object tree for the first iterate run-summary (same traversal
|
|
189
|
+
* semantics as `findReportInObject`, with circular-reference + depth guards).
|
|
190
|
+
*
|
|
191
|
+
* @param {unknown} obj
|
|
192
|
+
* @param {Set<unknown>} [seen]
|
|
193
|
+
* @param {number} [maxDepth=20]
|
|
194
|
+
* @returns {Record<string, unknown> | null}
|
|
195
|
+
*/
|
|
196
|
+
export function findRunSummaryInObject(obj, seen, maxDepth = 20) {
|
|
197
|
+
if (maxDepth <= 0) return null
|
|
198
|
+
if (!obj || typeof obj !== 'object') return null
|
|
199
|
+
|
|
200
|
+
const s = seen || new Set()
|
|
201
|
+
if (s.has(obj)) return null
|
|
202
|
+
s.add(obj)
|
|
203
|
+
|
|
204
|
+
if (isRunSummary(obj)) return /** @type {Record<string, unknown>} */ (obj)
|
|
205
|
+
|
|
206
|
+
if (Array.isArray(obj)) {
|
|
207
|
+
for (const item of obj) {
|
|
208
|
+
const found = findRunSummaryInObject(item, s, maxDepth - 1)
|
|
209
|
+
if (found) return found
|
|
210
|
+
}
|
|
211
|
+
return null
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const o = /** @type {Record<string, unknown>} */ (obj)
|
|
215
|
+
for (const key of Object.keys(o)) {
|
|
216
|
+
const val = o[key]
|
|
217
|
+
if (val && typeof val === 'object') {
|
|
218
|
+
const found = findRunSummaryInObject(val, s, maxDepth - 1)
|
|
219
|
+
if (found) return found
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return null
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Scan a session snapshot (or any object) for the latest iterate dry-run
|
|
228
|
+
* run-summary that exposes a meta-review verdict. Prefers the most recent.
|
|
229
|
+
*
|
|
230
|
+
* @param {unknown} session
|
|
231
|
+
* @returns {Record<string, unknown> | null}
|
|
232
|
+
*/
|
|
233
|
+
export function scanSessionForRunSummary(session) {
|
|
234
|
+
if (!session || typeof session !== 'object') return null
|
|
235
|
+
|
|
236
|
+
const direct = findRunSummaryInObject(session)
|
|
237
|
+
if (direct) return direct
|
|
238
|
+
|
|
239
|
+
const s = /** @type {Record<string, unknown>} */ (session)
|
|
240
|
+
|
|
241
|
+
// Common pattern: session.toolCalls[].result contains a run summary.
|
|
242
|
+
if (Array.isArray(s.toolCalls)) {
|
|
243
|
+
const calls = /** @type {Array<Record<string, unknown>>} */ (s.toolCalls)
|
|
244
|
+
for (let i = calls.length - 1; i >= 0; i--) {
|
|
245
|
+
const call = calls[i]
|
|
246
|
+
if (!call) continue
|
|
247
|
+
if (call.tool === 'workflow' || String(call.tool ?? '').endsWith('workflow')) {
|
|
248
|
+
const found = findRunSummaryInObject(call.result, undefined, 24)
|
|
249
|
+
if (found) return found
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Common pattern: assistant message content holding the workflow return.
|
|
255
|
+
if (Array.isArray(s.messages)) {
|
|
256
|
+
const msgs = /** @type {Array<Record<string, unknown>>} */ (s.messages)
|
|
257
|
+
for (let i = msgs.length - 1; i >= 0; i--) {
|
|
258
|
+
const msg = msgs[i]
|
|
259
|
+
if (!msg) continue
|
|
260
|
+
const found = findRunSummaryInObject(msg.content)
|
|
261
|
+
if (found) return found
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return null
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/**
|
|
269
|
+
* Extract a compact, UI-friendly verdict from a run-summary object.
|
|
270
|
+
* Returns null when the object is not a valid run-summary.
|
|
271
|
+
*
|
|
272
|
+
* @param {Record<string, unknown> | null | undefined} runSummary
|
|
273
|
+
* @returns {{ verdict: 'approved' | 'needs_revision', reportIssues: number, checksRun: number, converged: boolean, totalRounds: number, totalFindings: number } | null}
|
|
274
|
+
*/
|
|
275
|
+
export function extractVerdict(runSummary) {
|
|
276
|
+
if (!isRunSummary(runSummary)) return null
|
|
277
|
+
const o = /** @type {Record<string, unknown>} */ (runSummary)
|
|
278
|
+
const final = /** @type {Record<string, unknown>} */ (o.finalReport)
|
|
279
|
+
const meta = final.metaReview && typeof final.metaReview === 'object'
|
|
280
|
+
? /** @type {Record<string, unknown>} */ (final.metaReview)
|
|
281
|
+
: {}
|
|
282
|
+
const issues = Array.isArray(meta.issues) ? meta.issues : []
|
|
283
|
+
// `totalRounds` may be a bare number (dry-run returns `rounds`) or a count.
|
|
284
|
+
const roundsVal = o.rounds
|
|
285
|
+
const totalRounds = typeof roundsVal === 'number' ? roundsVal : (Array.isArray(roundsVal) ? roundsVal.length : 0)
|
|
286
|
+
return {
|
|
287
|
+
verdict: final.verdict === 'needs_revision' ? 'needs_revision' : 'approved',
|
|
288
|
+
reportIssues: issues.length,
|
|
289
|
+
checksRun: typeof meta.checksRun === 'number' ? meta.checksRun : 0,
|
|
290
|
+
converged: o.converged === true,
|
|
291
|
+
totalRounds,
|
|
292
|
+
totalFindings: typeof o.totalFindings === 'number' ? o.totalFindings : 0,
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
163
296
|
// ─── Normalization ───────────────────────────────────────────────────────────
|
|
164
297
|
|
|
165
298
|
/**
|
|
@@ -193,7 +326,9 @@ export function normalizeReport(report) {
|
|
|
193
326
|
}
|
|
194
327
|
|
|
195
328
|
// Compute summary if missing. Always build a NEW object so the input's
|
|
196
|
-
// summary (or any other field) is never mutated.
|
|
329
|
+
// summary (or any other field) is never mutated. `fixedCount` (normal mode
|
|
330
|
+
// only) is carried through so the dashboard fix-count metric survives
|
|
331
|
+
// normalization.
|
|
197
332
|
let summary = report.summary
|
|
198
333
|
if (!summary || typeof summary !== 'object') {
|
|
199
334
|
summary = computeSummaryFromFindings(findings)
|
|
@@ -209,6 +344,7 @@ export function normalizeReport(report) {
|
|
|
209
344
|
byDimension: s.byDimension && typeof s.byDimension === 'object'
|
|
210
345
|
? s.byDimension
|
|
211
346
|
: computed.byDimension,
|
|
347
|
+
...(typeof s.fixedCount === 'number' ? { fixedCount: s.fixedCount } : {}),
|
|
212
348
|
}
|
|
213
349
|
}
|
|
214
350
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iterate-plugin",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.3",
|
|
4
4
|
"description": "dsh plugin that turns the iterate skill into an autonomous closed-loop harness: plan -> parallel review xN -> atomic fixes -> validate -> loop -> auto-stop, plus a dry-run pure-review mode with multi-round convergence and a meta-review that audits the report and emits a final review report.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -53,8 +53,10 @@
|
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
55
|
"build": "tsc -p tsconfig.build.json",
|
|
56
|
-
"
|
|
56
|
+
"build:client": "node scripts/build-client.mjs",
|
|
57
|
+
"prepublishOnly": "npm run build && npm run build:client",
|
|
57
58
|
"typecheck": "tsc --noEmit",
|
|
59
|
+
"typecheck:client": "tsc --noEmit -p tsconfig.client.json",
|
|
58
60
|
"test": "tsx --test test/*.test.ts",
|
|
59
61
|
"test:validate": "tsx --test test/validate.test.ts"
|
|
60
62
|
},
|
|
@@ -67,6 +69,8 @@
|
|
|
67
69
|
"@deepseek-ai/dsh-session": "0.1.0-rc.6",
|
|
68
70
|
"@types/js-yaml": "4.0.9",
|
|
69
71
|
"@types/node": "22.15.0",
|
|
72
|
+
"@types/react": "19.2.2",
|
|
73
|
+
"esbuild": "0.25.12",
|
|
70
74
|
"tsx": "4.20.3",
|
|
71
75
|
"typescript": "5.9.3"
|
|
72
76
|
},
|