iterate-plugin 2.3.6

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 iterate-skill contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # iterate-plugin for DeepSeek Harness (dsh)
2
+
3
+ `iterate-plugin` 是 [iterate](https://github.com/iterate-skill/iterate-skill) 技能的 [DeepSeek Harness (dsh)](https://github.com/deepseek-ai/deepseek-harness) 插件,提供**自治闭环代码迭代**和**dry-run 纯多轮审查**能力。
4
+
5
+ ## 特性
6
+
7
+ | 功能 | dry-run 模式 | normal 模式 |
8
+ |------|-------------|------------|
9
+ | 多轮收敛反复审查 | ✅ | ✅ |
10
+ | 并行维度评审 | ✅ | ✅ |
11
+ | 确定性聚合去重/排序 | ✅ | ✅ |
12
+ | meta-review 报告一致性审计 | ✅ | ✅ |
13
+ | 零文件修改(只读) | ✅ | ❌ |
14
+ | 原子问题自动修复 | ❌ | ✅ |
15
+ | 每轮修复后验证 | ❌ | ✅ |
16
+ | 达标自停 | ✅ | ✅ |
17
+ | 只修改 atomic 问题,保留 architectural 留待后续 | ❌ | ✅ |
18
+
19
+ ## 安装
20
+
21
+ ### 从 npm 安装(发布后)
22
+
23
+ ```bash
24
+ dsh plugin --profile web add iterate-plugin
25
+ # 或
26
+ pnpm add iterate-plugin
27
+ ```
28
+
29
+ ### 本地开发 / 源码挂载
30
+
31
+ ```bash
32
+ dsh plugin --profile web add /Volumes/Eng-Dev/iterate-skill/harness/iterate-plugin
33
+ # 或
34
+ pnpm add /path/to/iterate-skill/harness/iterate-plugin
35
+ ```
36
+
37
+ 然后在你的 profile `cordis.patch.yml` 添加:
38
+
39
+ ```yaml
40
+ - insert:
41
+ - id: iterate-plugin
42
+ name: 'iterate-plugin'
43
+ ```
44
+
45
+ ## 使用
46
+
47
+ ### dry-run 模式(纯反复审查,不修改文件)
48
+
49
+ 当你想要 "只是反复审查,不修改文件",prompt 示例:
50
+
51
+ ```
52
+ dry-run review this project, find all issues across all dimensions
53
+ ```
54
+
55
+ 插件会自动触发 iterate 工作流:
56
+ 1. `plan` → 读取配置,生成评审计划
57
+ 2. `loop` → 每轮并行评审,只找新问题 → 确定性聚合去重 → 统计收敛 → 无新问题则停止
58
+ 3. `meta-review` → 审计报告一致性
59
+ 4. `report` → 输出最终结果
60
+
61
+ ### normal 模式(自治闭环迭代)
62
+
63
+ 当你想要 "iterate this project / fix the issues found",prompt 示例:
64
+
65
+ ```
66
+ iterate on this project, fix all atomic issues
67
+ ```
68
+
69
+ 工作流:
70
+ 1. `plan` → 读取配置
71
+ 2. `loop` → 并行评审 → 聚合去重 → 原子问题并行修复 → 执行验证命令 → 记录日志 → 无新问题则停止
72
+ 3. `report` → 输出修复统计
73
+
74
+ ## 项目配置
75
+
76
+ 在项目根目录放 `iterate.config.yaml`:
77
+
78
+ ```yaml
79
+ # 评审目标(例如 "提高代码质量,修复潜在bug,改善可维护性")
80
+ goal: "Improve code quality of the project"
81
+ # 评审维度(从本插件预定义维度选或自定义)
82
+ dimensions:
83
+ - correctness
84
+ - security
85
+ - performance
86
+ - maintainability
87
+ - code-style
88
+ # 最大评审轮次
89
+ max_rounds: 3
90
+ # 评审范围
91
+ review:
92
+ scope: full # full = 全项目,changed-only = 只看变更文件
93
+ # 已知故意不修复的问题(评审会过滤掉,不再重复报告)
94
+ personalization:
95
+ known_intentional:
96
+ - file: src/example.ts
97
+ line: 42
98
+ dimension: security
99
+ reason: "Intentional for demonstration"
100
+ # 验证命令(修复后自动跑,结果记入日志)
101
+ validation:
102
+ commands:
103
+ - npm test
104
+ - npm run typecheck
105
+ ```
106
+
107
+ ## 注册工具
108
+
109
+ 插件注册了 5 个工具:
110
+
111
+ | 工具 | 功能 |
112
+ |------|------|
113
+ | `iterate_config` | 读取并验证 `iterate.config.yaml` |
114
+ | `iterate_validate` | 运行白名单验证命令,返回结果 |
115
+ | `iterate_decision_log` | 追加决策日志(只追加,不改旧) |
116
+ | `iterate_context` | 读取 `SKILL.md` / `ITERATE.md` 上下文 |
117
+ | `iterate_review` | 确定性评审引擎:`plan` 生成计划,`aggregate` 聚合结论,`meta-review` 审计报告 |
118
+
119
+ ## 设计
120
+
121
+ 插件遵循 dsh "everything-is-a-plugin" 架构:
122
+ - 只做一件事:注入系统 prompt 教模型写 iterate workflow + 注册 5 个纯函数工具
123
+ - 所有 orchestration 通过 dsh 原生 `workflow` + `agent` + `parallel` 完成
124
+ - 核心逻辑(去重/过滤/排序/收敛/meta-audit)全部纯函数,可单元测试,无 I/O
125
+ - 遵循 iterate 原技能的设计原则:确定性收敛,可审计,最小权限
126
+
127
+ ## 运行测试
128
+
129
+ ```bash
130
+ cd harness/iterate-plugin
131
+ npm install
132
+ npm run typecheck
133
+ npm test
134
+ ```
135
+
136
+ 所有测试通过:
137
+ - 31 个单元测试全绿
138
+ - 覆盖去重、过滤、排序、多轮收敛、meta-review 审计
139
+ - 类型检查通过
140
+
141
+ ## License
142
+
143
+ MIT
@@ -0,0 +1,9 @@
1
+ # iterate-plugin bundle layer — applied when a profile lists this bundle.
2
+ # package.json declares `dsh.bundle.patch` pointing at this file.
3
+ #
4
+ # `name` is a PACKAGE NAME (resolved through the profile's node_modules or the
5
+ # installation fallback $DSH_HOME/profiles/node_modules), NOT a relative path.
6
+ # `iterate-plugin` is this package's main entry (→ src/index.ts).
7
+ - insert:
8
+ - id: iterate-plugin
9
+ name: iterate-plugin
package/package.json ADDED
@@ -0,0 +1,59 @@
1
+ {
2
+ "name": "iterate-plugin",
3
+ "version": "2.3.6",
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
+ "type": "module",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "dsh-plugin",
9
+ "deepseek-harness",
10
+ "dsh",
11
+ "iterate",
12
+ "code-review",
13
+ "autonomous",
14
+ "dry-run",
15
+ "cordis"
16
+ ],
17
+ "dsh": {
18
+ "bundle": {
19
+ "patch": "./cordis.patch.yml"
20
+ }
21
+ },
22
+ "files": [
23
+ "src",
24
+ "cordis.patch.yml",
25
+ "README.md",
26
+ "LICENSE"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./src/index.ts",
31
+ "default": "./src/index.ts"
32
+ }
33
+ },
34
+ "scripts": {
35
+ "typecheck": "tsc --noEmit",
36
+ "test": "tsx --test test/*.test.ts",
37
+ "test:script": "tsx --test test/script.test.ts",
38
+ "test:runtime": "tsx --test test/runtime.test.ts",
39
+ "test:loader": "tsx --test test/loader.test.ts",
40
+ "test:decisions": "tsx --test test/decisions.test.ts",
41
+ "test:validate": "tsx --test test/validate.test.ts"
42
+ },
43
+ "dependencies": {
44
+ "@deepseek-ai/cordis": "4.0.1",
45
+ "@deepseek-ai/dsh-tools": "0.1.0-rc.6",
46
+ "@deepseek-ai/dsh-workflow": "0.1.0-rc.6",
47
+ "@deepseek-ai/schemastery": "3.18.1",
48
+ "js-yaml": "4.3.1"
49
+ },
50
+ "devDependencies": {
51
+ "@types/js-yaml": "4.0.9",
52
+ "@types/node": "22.15.0",
53
+ "tsx": "4.20.3",
54
+ "typescript": "5.9.3"
55
+ },
56
+ "engines": {
57
+ "node": ">=20"
58
+ }
59
+ }
@@ -0,0 +1,162 @@
1
+ import { readFileSync } from 'node:fs'
2
+ import { join } from 'node:path'
3
+ import yaml from 'js-yaml'
4
+ import type { IterateConfig } from './types.ts'
5
+
6
+ /**
7
+ * Load and parse iterate.config.yaml from the project root.
8
+ * Returns null if the file is missing or invalid.
9
+ */
10
+ export function loadConfig(projectRoot: string): IterateConfig | null {
11
+ try {
12
+ const content = readFileSync(join(projectRoot, 'iterate.config.yaml'), 'utf-8')
13
+ const parsed = yaml.load(content) as Record<string, unknown>
14
+ if (!parsed || typeof parsed !== 'object') return null
15
+ return parsed as unknown as IterateConfig
16
+ } catch {
17
+ return null
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Sensible defaults for every config field. These are the "Master" config:
23
+ * when a project has no iterate.config.yaml (or only partial overrides), every
24
+ * missing key is filled from here so the plugin is usable out of the box while
25
+ * never inventing trusted validation commands (they must be configured).
26
+ */
27
+ export function defaultConfig(): IterateConfig {
28
+ return {
29
+ goal: 'Improve code quality and maintainability',
30
+ max_rounds: 7,
31
+ language: 'en',
32
+ dimensions: [
33
+ 'correctness',
34
+ 'security',
35
+ 'performance',
36
+ 'architecture',
37
+ 'style-tests',
38
+ 'tech-debt',
39
+ 'spec-compliance',
40
+ 'frontend-backend',
41
+ 'ui-ux',
42
+ ],
43
+ review: { scope: 'full' },
44
+ atomic: { max_lines: 20, max_adjacent_methods: 3 },
45
+ git: {
46
+ target_branch: 'main',
47
+ use_worktree: false,
48
+ push_per_round: false,
49
+ auto_merge: false,
50
+ },
51
+ validation: { command_whitelist: [], commands: {} },
52
+ reviewer: { output_schema_validation: true },
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Recursively merge `override` on top of `base`.
58
+ * - Missing keys in `base` are added from `override`.
59
+ * - Present keys in `override` win.
60
+ * - Plain objects are merged recursively; arrays and scalars are replaced
61
+ * wholesale by the override (arrays are NOT concatenated).
62
+ * Returns a NEW object; neither input is mutated.
63
+ */
64
+ export function mergeConfig(
65
+ base: Record<string, unknown>,
66
+ override: Record<string, unknown> | undefined,
67
+ ): Record<string, unknown> {
68
+ if (!override || typeof override !== 'object') return { ...base }
69
+ const out: Record<string, unknown> = { ...base }
70
+ for (const [key, value] of Object.entries(override)) {
71
+ if (value === undefined) continue
72
+ const baseValue = out[key]
73
+ if (
74
+ baseValue &&
75
+ typeof baseValue === 'object' &&
76
+ !Array.isArray(baseValue) &&
77
+ value &&
78
+ typeof value === 'object' &&
79
+ !Array.isArray(value)
80
+ ) {
81
+ out[key] = mergeConfig(
82
+ baseValue as Record<string, unknown>,
83
+ value as Record<string, unknown>,
84
+ )
85
+ } else {
86
+ out[key] = value
87
+ }
88
+ }
89
+ return out
90
+ }
91
+
92
+ /**
93
+ * Load the EFFECTIVE config for a project: project-root overrides merged on top
94
+ * of the built-in defaults ("Master + Overrides"). Never returns null — a
95
+ * project without a config file simply runs on the defaults (with an empty
96
+ * validation command set, so nothing untrusted can ever execute).
97
+ */
98
+ export function loadEffectiveConfig(projectRoot: string): {
99
+ config: IterateConfig
100
+ source: 'defaults' | 'override'
101
+ override: IterateConfig | null
102
+ } {
103
+ const override = loadConfig(projectRoot)
104
+ if (!override) {
105
+ return { config: defaultConfig(), source: 'defaults', override: null }
106
+ }
107
+ const merged = mergeConfig(
108
+ defaultConfig() as unknown as Record<string, unknown>,
109
+ override as unknown as Record<string, unknown>,
110
+ ) as unknown as IterateConfig
111
+ return { config: merged, source: 'override', override }
112
+ }
113
+
114
+ /**
115
+ * Check whether a command is in the predefined commands list.
116
+ * A command is allowed if it is EXACTLY (after trim) listed in any
117
+ * module's command array in `validation.commands`.
118
+ * This replaces the old prefix-based whitelist at runtime — the
119
+ * `command_whitelist` is still used for config-time validation only.
120
+ */
121
+ export function isCommandAllowed(command: string, predefinedCommands: string[]): boolean {
122
+ const trimmed = command.trim()
123
+ return predefinedCommands.includes(trimmed)
124
+ }
125
+
126
+ /**
127
+ * Flatten all commands from `validation.commands` into a single string array.
128
+ * Used for runtime exact-match checking.
129
+ */
130
+ export function flattenCommands(
131
+ commands: Record<string, string[]> | undefined,
132
+ ): string[] {
133
+ if (!commands || typeof commands !== 'object') return []
134
+ const out: string[] = []
135
+ for (const v of Object.values(commands)) {
136
+ if (Array.isArray(v)) out.push(...v)
137
+ }
138
+ return out
139
+ }
140
+
141
+ /**
142
+ * Validate that the config has all required fields.
143
+ * Returns an array of missing field paths.
144
+ */
145
+ export function validateConfig(config: unknown): string[] {
146
+ const errors: string[] = []
147
+ if (!config || typeof config !== 'object') {
148
+ errors.push('root')
149
+ return errors
150
+ }
151
+ const c = config as Record<string, unknown>
152
+ if (!c.goal) errors.push('goal')
153
+ if (!Array.isArray(c.dimensions)) errors.push('dimensions')
154
+ if (!c.validation || typeof c.validation !== 'object') {
155
+ errors.push('validation')
156
+ } else {
157
+ const v = c.validation as Record<string, unknown>
158
+ if (!Array.isArray(v.command_whitelist)) errors.push('validation.command_whitelist')
159
+ if (!v.commands || typeof v.commands !== 'object') errors.push('validation.commands')
160
+ }
161
+ return errors
162
+ }
package/src/index.ts ADDED
@@ -0,0 +1,50 @@
1
+ /**
2
+ * iterate-plugin — dsh plugin for the iterate autonomous closed-loop workflow
3
+ *
4
+ * Architecture:
5
+ * - The plugin registers 4 tools (config, validate, decision-log, context)
6
+ * - The plugin injects a system prompt section teaching the iterate workflow pattern
7
+ * - The model (prompted by the skill) writes a workflow script using dsh's `workflow` tool
8
+ * - The workflow script uses `agent()` / `parallel()` / `phase()` / `log()` to orchestrate
9
+ * - Subagents use the 4 tools to do real work (read config, run validation, log decisions)
10
+ *
11
+ * Tool invocation model:
12
+ * - Workflow script CANNOT call tools directly (sandboxed vm, no Node API)
13
+ * - Workflow script spawns subagents via `agent(prompt, opts)`
14
+ * - Subagents are full agent sessions with access to all registered tools
15
+ * - The script is pure orchestration: fan-out, aggregate, loop, stop
16
+ *
17
+ * Key files:
18
+ * - src/index.ts — Plugin entry: register tools + inject skill prompt
19
+ * - src/tools/ — 4 tool implementations
20
+ * - src/config-loader.ts — YAML config loading
21
+ * - src/types.ts — Shared types
22
+ */
23
+
24
+ import type { Context } from '@deepseek-ai/cordis'
25
+ import { registerConfigTool } from './tools/config.ts'
26
+ import { registerValidateTool } from './tools/validate.ts'
27
+ import { registerDecisionLogTool } from './tools/decision-log.ts'
28
+ import { registerContextTool } from './tools/context.ts'
29
+ import { registerReviewTool } from './tools/review.ts'
30
+ import { ITERATE_SKILL_PROMPT } from './skill-prompt.ts'
31
+
32
+ export const name = 'iterate-plugin'
33
+ export const inject = ['tools', 'systemPrompt']
34
+
35
+ export function apply(ctx: Context): void {
36
+ // 1. Register the 5 core tools
37
+ registerConfigTool(ctx)
38
+ registerValidateTool(ctx)
39
+ registerDecisionLogTool(ctx)
40
+ registerContextTool(ctx)
41
+ registerReviewTool(ctx)
42
+
43
+ // 2. Inject the iterate skill prompt as a system prompt section
44
+ // This teaches the model how to write iterate workflow scripts using the tools.
45
+ ctx.systemPrompt.section({
46
+ name: 'iterate-skill',
47
+ order: 100,
48
+ text: ITERATE_SKILL_PROMPT,
49
+ })
50
+ }