iterate-plugin 3.7.0 → 3.9.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 +43 -8
- package/README.zh-CN.md +26 -5
- package/dist/file-lock.js +75 -14
- package/dist/git-scope.js +22 -2
- package/dist/index.js +9 -4
- package/dist/report-render.js +452 -0
- package/dist/session-record.js +246 -0
- package/dist/skill-prompt.js +4 -3
- package/dist/tools/checkpoint.js +24 -17
- package/dist/tools/config.js +42 -33
- package/dist/tools/decision-log.js +13 -93
- package/dist/tools/report.js +239 -0
- package/dist/tools/transcript.js +260 -53
- package/dist/tools/triage.js +52 -45
- package/dist/transcript.js +2 -2
- package/lib/client.js +486 -22
- package/lib/parse.js +29 -6
- package/package.json +38 -13
- package/src/client/i18n.ts +6 -0
- package/src/client/index.ts +66 -4
- package/src/file-lock.ts +84 -14
- package/src/git-scope.ts +21 -2
- package/src/index.ts +9 -4
- package/src/report-render.ts +578 -0
- package/src/session-record.ts +333 -0
- package/src/skill-prompt.ts +4 -3
- package/src/tools/checkpoint.ts +23 -16
- package/src/tools/config.ts +40 -31
- package/src/tools/decision-log.ts +12 -80
- package/src/tools/report.ts +244 -0
- package/src/tools/transcript.ts +271 -52
- package/src/tools/triage.ts +47 -40
- package/src/transcript.ts +2 -2
- package/src/types.ts +1 -1
package/src/tools/triage.ts
CHANGED
|
@@ -6,6 +6,7 @@ import yaml from 'js-yaml'
|
|
|
6
6
|
import { resolveProjectRootForExec } from '../config-loader.ts'
|
|
7
7
|
import { writeTextAtomic } from '../atomic-fs.ts'
|
|
8
8
|
import { configBackupSuffix } from '../config-write.ts'
|
|
9
|
+
import { withProjectLock } from '../file-lock.ts'
|
|
9
10
|
import { WHOLE_FILE_LINE } from '../evidence.ts'
|
|
10
11
|
import type { KnownIntentional } from '../types.ts'
|
|
11
12
|
|
|
@@ -268,56 +269,62 @@ function applyEntries(
|
|
|
268
269
|
error: string
|
|
269
270
|
} {
|
|
270
271
|
const configPath = join(projectRoot, CONFIG_FILE)
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
272
|
+
// Serialize against `iterate_config write` (SAME lock name) so a triage apply
|
|
273
|
+
// and a config write on one project cannot interleave their read-modify-write
|
|
274
|
+
// and silently drop each other's updates. The lock spans read → merge →
|
|
275
|
+
// backup → write → backup-prune so the whole cycle is atomic.
|
|
276
|
+
return withProjectLock(projectRoot, 'config-write', () => {
|
|
277
|
+
let config: Record<string, unknown>
|
|
278
|
+
try {
|
|
279
|
+
config = readConfigFile(configPath)
|
|
280
|
+
} catch (err) {
|
|
281
|
+
// The file exists but is malformed — refuse to overwrite user data.
|
|
282
|
+
return { ok: false, error: `Failed to read config: ${String(err)}` }
|
|
283
|
+
}
|
|
284
|
+
const existing = readKnownIntentional(config)
|
|
285
|
+
const { merged, added, skipped, dropped } = mergeKnownIntentional(existing, incoming)
|
|
286
|
+
const nextConfig = buildConfigWithKnownIntentional(config, merged)
|
|
281
287
|
|
|
282
|
-
|
|
283
|
-
|
|
288
|
+
const hadFile = existsSync(configPath)
|
|
289
|
+
const backupPath = hadFile ? `${configPath}.bak-${backupSuffix()}` : null
|
|
290
|
+
|
|
291
|
+
if (backupPath) {
|
|
292
|
+
try {
|
|
293
|
+
copyFileSync(configPath, backupPath)
|
|
294
|
+
} catch (err) {
|
|
295
|
+
return {
|
|
296
|
+
ok: false,
|
|
297
|
+
error: `Failed to create backup: ${String(err)}`,
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
284
301
|
|
|
285
|
-
|
|
302
|
+
const yamlText = yaml.dump(nextConfig, { noRefs: true })
|
|
286
303
|
try {
|
|
287
|
-
|
|
304
|
+
writeTextAtomic(configPath, yamlText)
|
|
288
305
|
} catch (err) {
|
|
306
|
+
// Rollback: restore the backup, or REMOVE the file we just created when
|
|
307
|
+
// there was no prior config — an empty file left behind would poison all
|
|
308
|
+
// future config reads (empty YAML is not a valid mapping).
|
|
309
|
+
let rollbackError = ''
|
|
310
|
+
try {
|
|
311
|
+
if (backupPath) copyFileSync(backupPath, configPath)
|
|
312
|
+
else if (existsSync(configPath)) rmSync(configPath, { force: true })
|
|
313
|
+
} catch (rbErr) {
|
|
314
|
+
rollbackError = `; rollback also failed: ${String(rbErr)}`
|
|
315
|
+
}
|
|
289
316
|
return {
|
|
290
317
|
ok: false,
|
|
291
|
-
error: `Failed to
|
|
318
|
+
error: `Failed to write config: ${String(err)}${rollbackError}`,
|
|
292
319
|
}
|
|
293
320
|
}
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
const yamlText = yaml.dump(nextConfig, { noRefs: true })
|
|
297
|
-
try {
|
|
298
|
-
writeTextAtomic(configPath, yamlText)
|
|
299
|
-
} catch (err) {
|
|
300
|
-
// Rollback: restore the backup, or REMOVE the file we just created when
|
|
301
|
-
// there was no prior config — an empty file left behind would poison all
|
|
302
|
-
// future config reads (empty YAML is not a valid mapping).
|
|
303
|
-
let rollbackError = ''
|
|
304
|
-
try {
|
|
305
|
-
if (backupPath) copyFileSync(backupPath, configPath)
|
|
306
|
-
else if (existsSync(configPath)) rmSync(configPath, { force: true })
|
|
307
|
-
} catch (rbErr) {
|
|
308
|
-
rollbackError = `; rollback also failed: ${String(rbErr)}`
|
|
309
|
-
}
|
|
310
|
-
return {
|
|
311
|
-
ok: false,
|
|
312
|
-
error: `Failed to write config: ${String(err)}${rollbackError}`,
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
321
|
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
322
|
+
// Success: bound the accumulation of timestamped backups so a long-lived
|
|
323
|
+
// project never collects an unbounded pile of config snapshots.
|
|
324
|
+
if (backupPath) pruneOldConfigBackups(configPath)
|
|
319
325
|
|
|
320
|
-
|
|
326
|
+
return { ok: true, added, skipped, dropped, count: merged.length, configPath, backupPath }
|
|
327
|
+
})
|
|
321
328
|
}
|
|
322
329
|
|
|
323
330
|
/**
|
package/src/transcript.ts
CHANGED
|
@@ -565,7 +565,7 @@ export class ReviewTranscriptBuilder {
|
|
|
565
565
|
return
|
|
566
566
|
}
|
|
567
567
|
this.checkpoint = {
|
|
568
|
-
mode: state.mode === 'dry-run' || state.mode === 'normal' ? state.mode :
|
|
568
|
+
mode: state.mode === 'dry-run' || state.mode === 'normal' ? state.mode : null,
|
|
569
569
|
round: typeof state.round === 'number' ? state.round : 0,
|
|
570
570
|
maxRounds: typeof state.maxRounds === 'number' ? state.maxRounds : 0,
|
|
571
571
|
fixedCount: typeof state.fixedCount === 'number' ? state.fixedCount : 0,
|
|
@@ -804,7 +804,7 @@ function normalizeCheckpointRow(input: unknown): TranscriptCheckpoint | null {
|
|
|
804
804
|
const round = typeof c.round === 'number' && Number.isFinite(c.round) ? c.round : 0
|
|
805
805
|
if (round <= 0) return null
|
|
806
806
|
return {
|
|
807
|
-
mode: c.mode === 'dry-run' || c.mode === 'normal' ? c.mode :
|
|
807
|
+
mode: c.mode === 'dry-run' || c.mode === 'normal' ? c.mode : null,
|
|
808
808
|
round: clampRoundValue(c.round),
|
|
809
809
|
maxRounds: clampCountValue(c.maxRounds),
|
|
810
810
|
fixedCount: clampCountValue(c.fixedCount),
|
package/src/types.ts
CHANGED
|
@@ -320,7 +320,7 @@ export interface TranscriptEntry {
|
|
|
320
320
|
|
|
321
321
|
/** Checkpoint summary surfaced for resume actions (F5). */
|
|
322
322
|
export interface TranscriptCheckpoint {
|
|
323
|
-
mode: 'dry-run' | 'normal'
|
|
323
|
+
mode: 'dry-run' | 'normal' | null
|
|
324
324
|
round: number
|
|
325
325
|
maxRounds: number
|
|
326
326
|
fixedCount: number
|