@mison/ling 1.0.2 → 1.1.1

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.
@@ -27,6 +27,23 @@ trigger: always_on
27
27
 
28
28
  ---
29
29
 
30
+ ## Spec 协议(任务跟踪 CSV 驱动)
31
+
32
+ 当工作区存在 `issues.csv` 时,视其为任务跟踪文件(单一事实源),并遵循:
33
+
34
+ 1. 读取 `issues.csv` 并只锁定一个原子任务。
35
+ 2. 将任务状态从“未开始”改为“进行中”,再开始开发。
36
+ 3. 修改完成后运行最小充分验证,并记录证据。
37
+ 4. 自审通过后将状态改为“已完成”。
38
+ 5. 若验证失败,先修复或回退,不得静默降级。
39
+
40
+ 模板参考:
41
+
42
+ - 优先:`.ling/spec/templates/driver-prompt.md`
43
+ - 其次:`~/.ling/spec/templates/driver-prompt.md`(若已启用全局 Spec Profile)
44
+
45
+ ---
46
+
30
47
  ## 请求分类器(第 1 步)
31
48
 
32
49
  **在执行任何动作前,先对请求分类:**
@@ -143,24 +143,34 @@ File to edit: UserService.ts
143
143
 
144
144
  > [CRITICAL] **核心要求:** 每个代理完成后仅运行所属技能脚本。
145
145
 
146
+ ### 脚本路径约定
147
+
148
+ 下表中的 `<skills_root>` 代表 Skills 根目录,常见取值:
149
+
150
+ - Ling 仓库源码:`.agents/skills`
151
+ - Gemini 工作区:`.agent/skills`
152
+ - Codex 全局:`~/.codex/skills`
153
+ - Gemini CLI 全局:`~/.gemini/skills`
154
+ - Antigravity 全局:`~/.gemini/antigravity/skills`
155
+
146
156
  ### 代理 -> 脚本映射
147
157
 
148
158
  | 代理 | 脚本 | 命令 |
149
159
  |-------|--------|---------|
150
- | **frontend-specialist** | UX Audit | `python .agent/skills/frontend-design/scripts/ux_audit.py .` |
151
- | **frontend-specialist** | A11y Check | `python .agent/skills/frontend-design/scripts/accessibility_checker.py .` |
152
- | **backend-specialist** | API Validator | `python .agent/skills/api-patterns/scripts/api_validator.py .` |
153
- | **mobile-developer** | Mobile Audit | `python .agent/skills/mobile-design/scripts/mobile_audit.py .` |
154
- | **database-architect** | Schema Validate | `python .agent/skills/database-design/scripts/schema_validator.py .` |
155
- | **security-auditor** | Security Scan | `python .agent/skills/vulnerability-scanner/scripts/security_scan.py .` |
156
- | **seo-specialist** | SEO Check | `python .agent/skills/seo-fundamentals/scripts/seo_checker.py .` |
157
- | **seo-specialist** | GEO Check | `python .agent/skills/geo-fundamentals/scripts/geo_checker.py .` |
158
- | **performance-optimizer** | Lighthouse | `python .agent/skills/performance-profiling/scripts/lighthouse_audit.py <url>` |
159
- | **test-engineer** | Test Runner | `python .agent/skills/testing-patterns/scripts/test_runner.py .` |
160
- | **test-engineer** | Playwright | `python .agent/skills/webapp-testing/scripts/playwright_runner.py <url>` |
161
- | **Any agent** | Lint Check | `python .agent/skills/lint-and-validate/scripts/lint_runner.py .` |
162
- | **Any agent** | Type Coverage | `python .agent/skills/lint-and-validate/scripts/type_coverage.py .` |
163
- | **Any agent** | i18n Check | `python .agent/skills/i18n-localization/scripts/i18n_checker.py .` |
160
+ | **frontend-specialist** | UX Audit | `python <skills_root>/frontend-design/scripts/ux_audit.py .` |
161
+ | **frontend-specialist** | A11y Check | `python <skills_root>/frontend-design/scripts/accessibility_checker.py .` |
162
+ | **backend-specialist** | API Validator | `python <skills_root>/api-patterns/scripts/api_validator.py .` |
163
+ | **mobile-developer** | Mobile Audit | `python <skills_root>/mobile-design/scripts/mobile_audit.py .` |
164
+ | **database-architect** | Schema Validate | `python <skills_root>/database-design/scripts/schema_validator.py .` |
165
+ | **security-auditor** | Security Scan | `python <skills_root>/vulnerability-scanner/scripts/security_scan.py .` |
166
+ | **seo-specialist** | SEO Check | `python <skills_root>/seo-fundamentals/scripts/seo_checker.py .` |
167
+ | **seo-specialist** | GEO Check | `python <skills_root>/geo-fundamentals/scripts/geo_checker.py .` |
168
+ | **performance-optimizer** | Lighthouse | `python <skills_root>/performance-profiling/scripts/lighthouse_audit.py <url>` |
169
+ | **test-engineer** | Test Runner | `python <skills_root>/testing-patterns/scripts/test_runner.py .` |
170
+ | **test-engineer** | Playwright | `python <skills_root>/webapp-testing/scripts/playwright_runner.py <url>` |
171
+ | **Any agent** | Lint Check | `python <skills_root>/lint-and-validate/scripts/lint_runner.py .` |
172
+ | **Any agent** | Type Coverage | `python <skills_root>/lint-and-validate/scripts/type_coverage.py .` |
173
+ | **Any agent** | i18n Check | `python <skills_root>/i18n-localization/scripts/i18n_checker.py .` |
164
174
 
165
175
  > [FAIL] **错误做法:** `test-engineer` 运行 `ux_audit.py`
166
176
  > [OK] **正确做法:** `frontend-specialist` 运行 `ux_audit.py`
@@ -18,7 +18,11 @@
18
18
 
19
19
  | 范围 | 路径 | 说明 |
20
20
  |---------|-----------|-------|
21
- | **Workspace(工作区)** | `<workspace-root>/.agent/skills/` | 仅作用于当前项目 |
21
+ | **Ling 仓库源码(Canonical)** | `<ling-repo>/.agents/skills/` | 模板源,用于构建与分发 |
22
+ | **Workspace(工作区)** | `<workspace-root>/.agent/skills/` | 仅作用于当前项目(Gemini/Antigravity) |
23
+ | **Global(全局)** | `~/.codex/skills/`、`~/.gemini/skills/`、`~/.gemini/antigravity/skills/` | 跨项目复用(按目标工具读取) |
24
+
25
+ > 约定:下文示例使用 `<skills_root>` 表示 Skills 根目录;请按你的安装方式替换为上表路径。
22
26
 
23
27
  ### 技能目录结构
24
28
 
@@ -39,7 +43,7 @@ my-skill/
39
43
  ### 步骤 1:创建目录
40
44
 
41
45
  ```bash
42
- mkdir -p .agent/skills/code-review
46
+ mkdir -p <skills_root>/code-review
43
47
  ```
44
48
 
45
49
  ### 步骤 2:创建 SKILL.md
@@ -119,12 +123,12 @@ Agent(智能体)会自动识别 `code-review` 技能,加载信息并按指
119
123
  ### 步骤 1:创建目录
120
124
 
121
125
  ```bash
122
- mkdir -p .agent/skills/license-header-adder/resources
126
+ mkdir -p <skills_root>/license-header-adder/resources
123
127
  ```
124
128
 
125
129
  ### 步骤 2:创建模板文件
126
130
 
127
- **`.agent/skills/license-header-adder/resources/HEADER.txt`**:
131
+ **`<skills_root>/license-header-adder/resources/HEADER.txt`**:
128
132
 
129
133
  ```
130
134
  /*
@@ -136,7 +140,7 @@ mkdir -p .agent/skills/license-header-adder/resources
136
140
 
137
141
  ### 步骤 3:创建 SKILL.md
138
142
 
139
- **`.agent/skills/license-header-adder/SKILL.md`**:
143
+ **`<skills_root>/license-header-adder/SKILL.md`**:
140
144
 
141
145
  ```markdown
142
146
  ---
package/CHANGELOG.md CHANGED
@@ -7,6 +7,31 @@
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [ling-1.1.1] - 2026-03-14
11
+
12
+ ### 新增
13
+
14
+ - Spec 项目级工作区初始化与诊断:新增 `ling spec init` / `ling spec doctor`,在工作区落盘 `.ling/spec` 资产与 `issues.csv` 校验。
15
+ - Spec Profiles 资产:全局与项目级均包含 `profiles/`,并纳入完整性校验与回退语义。
16
+ - Spec 资产分支拉取:`ling spec init --branch <name>` 支持从指定分支的 `.spec/` 拉取 templates/references/profiles。
17
+
18
+ ### 变更
19
+
20
+ - Spec 状态判定强化:`ling spec status` 增加文件级完整性校验,且在 `state.json` 缺失但检测到残留 artifacts 时返回 `broken` 并给出修复提示。
21
+ - CI 改为 npm:CI 矩阵不再依赖 bun,统一使用 `npm ci/test/run` 执行验证。
22
+
23
+ ## [ling-1.1.0] - 2026-03-13
24
+
25
+ ### 新增
26
+
27
+ - 已有资产冲突处理:`init/update/update-all/global sync/spec enable` 在交互终端逐项确认(保留 / 备份后移除 / 直接移除),并支持按资产类别复用选择。
28
+ - 预备份与回退:项目级覆盖前快照落盘到 `.agent-backup/.agents-backup`;全局与 Spec 维持快照备份路径。
29
+
30
+ ### 维护
31
+
32
+ - ag-kit 更名清理:内部 CLI 入口更名为 `bin/ling-cli.js`,并移除 `AG_KIT_*` 兼容环境变量与 `~/.ag-kit` 控制目录迁移逻辑。
33
+ - 托管区块标记更名为 `LING MANAGED BLOCK`,并兼容识别旧 `AG-KIT` 标记后自动迁移。
34
+
10
35
  ## [ling-1.0.2] - 2026-03-13
11
36
 
12
37
  ### 变更
@@ -22,7 +47,7 @@
22
47
 
23
48
  ### 维护
24
49
 
25
- - CLI 入口权限:`bin/ling.js` 设置为可执行,保持与 `bin/ag-kit.js` 一致。
50
+ - CLI 入口权限:`bin/ling.js` 设置为可执行,保持与 `bin/ling-cli.js` 一致。
26
51
 
27
52
  ## [ling-1.0.0] - 2026-03-13
28
53
 
@@ -35,7 +60,6 @@
35
60
 
36
61
  - 品牌更名基础设施:
37
62
  - 主命令切换为 `ling`
38
- - `ag-kit` 保留兼容入口
39
63
  - 目标目录结构:`gemini -> .agent/`,`codex -> .agents/`
40
64
  - 控制目录、索引和备份默认迁移到 `~/.ling/`
41
65
  - `antigravity.rules` 收敛为 `ling.rules`
@@ -56,7 +80,9 @@
56
80
 
57
81
  本项目在 Ling 重启前的 2.x/3.x 版本记录已冻结,不再维护。
58
82
 
59
- [Unreleased]: https://github.com/MisonL/Ling/compare/ling-1.0.2...HEAD
83
+ [Unreleased]: https://github.com/MisonL/Ling/compare/ling-1.1.1...HEAD
84
+ [ling-1.1.1]: https://github.com/MisonL/Ling/releases/tag/ling-1.1.1
85
+ [ling-1.1.0]: https://github.com/MisonL/Ling/releases/tag/ling-1.1.0
60
86
  [ling-1.0.2]: https://github.com/MisonL/Ling/releases/tag/ling-1.0.2
61
87
  [ling-1.0.1]: https://github.com/MisonL/Ling/releases/tag/ling-1.0.1
62
88
  [ling-1.0.0]: https://github.com/MisonL/Ling/releases/tag/ling-1.0.0
package/README.md CHANGED
@@ -6,25 +6,12 @@
6
6
 
7
7
  > 面向 Gemini CLI、Antigravity 与 Codex 的中文 AI Agent 模板工具包,提供 Skills、Agents、Workflows 与 CLI 的一键安装、更新和治理。
8
8
 
9
- ## 致谢
10
-
11
- 本项目吸收并整合了社区项目的经验与资产,感谢:
12
-
13
- - [vudovn/antigravity-kit](https://github.com/vudovn/antigravity-kit)
14
- - [2217173240/Coding-Agent-prompt-best-practice](https://github.com/2217173240/Coding-Agent-prompt-best-practice)
15
-
16
9
  ## 快速安装
17
10
 
18
11
  ```bash
19
12
  npm install -g @mison/ling
20
13
  ```
21
14
 
22
- 或使用 Bun:
23
-
24
- ```bash
25
- bun install -g @mison/ling
26
- ```
27
-
28
15
  然后在你的目标项目中初始化:
29
16
 
30
17
  ```bash
@@ -83,6 +70,27 @@ ling global status
83
70
  - npm 包版本遵循 SemVer(`package.json`)
84
71
  - git tag 与 CLI `--version` 显示使用 `ling-<SemVer>`(例如 `ling-1.0.0`)
85
72
 
73
+ ### 已有资产冲突处理(交互确认 + 备份回退)
74
+
75
+ 当检测到目标目录已存在且内容不同(或 Codex 目录存在漂移、缺失 `manifest.json`、包含未知文件)时,`ling` 会在交互终端中逐项询问处理方式:
76
+
77
+ - `k` 保留(跳过此资产)
78
+ - `b` 备份后移除(推荐)
79
+ - `r` 直接移除(不备份)
80
+
81
+ 同一类别的冲突可选择“一键复用”,后续遇到同类资产不再重复询问。
82
+
83
+ 备份落盘位置:
84
+ - 项目级预备份:
85
+ - Gemini:`<project>/.agent-backup/<timestamp>/preflight/.agent/`
86
+ - Codex:`<project>/.agents-backup/<timestamp>/preflight/.agents/` 或 `<project>/.agents-backup/<timestamp>/preflight/.codex/`
87
+ - 全局 Skills:`$HOME/.ling/backups/global/<timestamp>/...`
88
+ - Spec Profile:`$HOME/.ling/backups/spec/<timestamp>/before/...`
89
+
90
+ 非交互环境(无 TTY 或 `--non-interactive`)不会进入询问:
91
+ - `update`/`update-all`/`global sync`/`spec enable` 在需要覆盖时默认执行“备份后覆盖”
92
+ - `init` 在检测到已有资产且未指定 `--force` 时会报错提示改用交互终端或显式 `--force`
93
+
86
94
  ### Spec Profile(可选进阶能力)
87
95
 
88
96
  - 默认关闭,不随 `ling init / update / global sync` 自动安装
@@ -138,7 +146,7 @@ ling spec disable --target codex
138
146
  | 组件 | 数量 | 描述 |
139
147
  | --- | --- | --- |
140
148
  | Agents(智能体) | 20 | 专家级 AI 人设(前端、后端、安全、产品、QA 等) |
141
- | Skills(技能) | 37 | 特定领域的知识模块 |
149
+ | Skills(技能) | 38 | 特定领域的知识模块 |
142
150
  | Workflows(工作流) | 12 | 斜杠命令流程 |
143
151
 
144
152
  ## 使用方法
@@ -263,21 +271,20 @@ ling exclude remove --path /path/to/dir # 删除排除路径
263
271
  ### 开发维护命令
264
272
 
265
273
  ```bash
266
- bun run clean # 清理本地生成产物(如 web/.next、web/node_modules)
267
- bun run clean:dry-run # 预览将被清理的路径
268
- bun run test # 只执行 tests/ 目录下测试
269
- bun run health-check # 一键执行全链路健康复检
274
+ npm run clean # 清理本地生成产物(如 web/.next、web/node_modules)
275
+ npm run clean:dry-run # 预览将被清理的路径
276
+ npm test # 执行 tests/ 目录下测试
277
+ npm run health-check # 一键执行全链路健康复检
270
278
  ```
271
279
 
272
280
  如果你在 `web/` 子项目内开发,可按需执行:
273
281
 
274
282
  ```bash
275
- bun install --cwd web
276
- bun run lint --cwd web
283
+ cd web
284
+ npm install
285
+ npm run lint
277
286
  ```
278
287
 
279
- > 说明:若你通过 `bun install -g` 安装 CLI,Bun 默认会阻止本包 `postinstall`。上游同名包冲突提示会在首次执行 `ling init/update/update-all/global sync` 时给出。
280
-
281
288
  ## 卸载
282
289
 
283
290
  ### 卸载本机全局 CLI
@@ -353,6 +360,13 @@ ling exclude add --path /path/to/your-project
353
360
  </tr>
354
361
  </table>
355
362
 
363
+ ## 致谢
364
+
365
+ 本项目吸收并整合了社区项目的经验与资产,感谢:
366
+
367
+ - [vudovn/antigravity-kit](https://github.com/vudovn/antigravity-kit)
368
+ - [2217173240/Coding-Agent-prompt-best-practice](https://github.com/2217173240/Coding-Agent-prompt-best-practice)
369
+
356
370
  ## 许可证
357
371
 
358
- MIT © Vudovn, Mison
372
+ MIT © [vudovn](https://github.com/vudovn), [Mison](https://github.com/MisonL), [2217173240](https://github.com/2217173240)
@@ -140,11 +140,11 @@ class CodexAdapter extends BaseAdapter {
140
140
  if (!isCodexPrebuilt) {
141
141
  if (hasSkillsDir) {
142
142
  this.log("[build] 检测到模板目录格式,正在构建 Codex 结构...");
143
- const mockRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-build-root-"));
143
+ const mockRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ling-build-root-"));
144
144
  const mockAgents = path.join(mockRoot, ".agents");
145
145
  this._copyDir(installSource, mockAgents);
146
146
 
147
- buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-build-out-"));
147
+ buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ling-build-out-"));
148
148
  CodexBuilder.build(mockRoot, buildTemp);
149
149
  installSource = buildTemp;
150
150
  sourceLabel = `${sourceLabel}:compiled`;
@@ -157,7 +157,7 @@ class CodexAdapter extends BaseAdapter {
157
157
  };
158
158
  } else if (hasAgentsRoot) {
159
159
  this.log("[build] 检测到仓库根目录格式(.agents),正在构建 Codex 结构...");
160
- buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-build-out-"));
160
+ buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ling-build-out-"));
161
161
  CodexBuilder.build(installSource, buildTemp);
162
162
  installSource = buildTemp;
163
163
  sourceLabel = `${sourceLabel}:compiled`;
@@ -169,11 +169,11 @@ class CodexAdapter extends BaseAdapter {
169
169
  };
170
170
  } else if (hasLegacyAgentRoot) {
171
171
  this.log("[build] 检测到旧版仓库根目录格式(.agent),正在构建 Codex 结构...");
172
- const mockRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-build-root-"));
172
+ const mockRoot = fs.mkdtempSync(path.join(os.tmpdir(), "ling-build-root-"));
173
173
  const mockAgents = path.join(mockRoot, ".agents");
174
174
  this._copyDir(path.join(installSource, ".agent"), mockAgents);
175
175
 
176
- buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-build-out-"));
176
+ buildTemp = fs.mkdtempSync(path.join(os.tmpdir(), "ling-build-out-"));
177
177
  CodexBuilder.build(mockRoot, buildTemp);
178
178
  installSource = buildTemp;
179
179
  sourceLabel = `${sourceLabel}:compiled`;
@@ -191,7 +191,7 @@ class CodexAdapter extends BaseAdapter {
191
191
  }
192
192
 
193
193
  _createStaging(installSource, sourceLabel) {
194
- const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-codex-stage-"));
194
+ const stagingDir = fs.mkdtempSync(path.join(os.tmpdir(), "ling-codex-stage-"));
195
195
  this._copyDir(installSource, stagingDir);
196
196
 
197
197
  const manifestPath = path.join(stagingDir, "manifest.json");
@@ -70,7 +70,7 @@ class GeminiAdapter extends BaseAdapter {
70
70
  }
71
71
 
72
72
  if (this._samePath(installSource, targetDir) && !this.options.dryRun) {
73
- const tempSource = fs.mkdtempSync(path.join(os.tmpdir(), "ag-kit-gemini-src-"));
73
+ const tempSource = fs.mkdtempSync(path.join(os.tmpdir(), "ling-gemini-src-"));
74
74
  this._copyDir(installSource, tempSource);
75
75
  const oldCleanup = cleanup;
76
76
  cleanup = () => {
@@ -37,6 +37,7 @@ class RuleGenerator {
37
37
  agentsMd += `1. Managed resources are synchronized under \`.agents/skills\`.\n`;
38
38
  agentsMd += `2. Do not rename managed skill folders manually.\n`;
39
39
  agentsMd += `3. Use \`ling doctor --target codex --fix\` to recover missing managed artifacts.\n`;
40
+ agentsMd += `4. If \`issues.csv\` exists, treat it as the task tracking source of truth and keep at most one task in \`进行中\`.\n`;
40
41
 
41
42
  // 3. Generate risk controls
42
43
  let lingRules = `# Ling Risk Controls (Codex Managed)\n\n`;
@@ -15,6 +15,10 @@ function askQuestion(rl, query) {
15
15
  });
16
16
  }
17
17
 
18
+ function isInteractiveTerminal() {
19
+ return Boolean(process.stdin.isTTY && process.stdout.isTTY);
20
+ }
21
+
18
22
  /**
19
23
  * Prompt user to select targets from a list.
20
24
  * Supports multiple selection by comma separated numbers or names.
@@ -27,6 +31,10 @@ async function selectTargets(options) {
27
31
  throw new Error("非交互模式下必须通过 --target 或 --targets 指定目标");
28
32
  }
29
33
 
34
+ if (!isInteractiveTerminal()) {
35
+ throw new Error("当前环境不是交互终端,请通过 --target 或 --targets 指定目标");
36
+ }
37
+
30
38
  const rl = createInterface();
31
39
 
32
40
  try {
@@ -60,6 +68,65 @@ async function selectTargets(options) {
60
68
  }
61
69
  }
62
70
 
71
+ /**
72
+ * Create an interactive prompter for resolving asset conflicts.
73
+ * @param {object} options CLI options
74
+ * @returns {{resolveConflict: function, close: function}|null}
75
+ */
76
+ function createConflictPrompter(options) {
77
+ if (options.nonInteractive || !isInteractiveTerminal()) {
78
+ return null;
79
+ }
80
+
81
+ const rl = createInterface();
82
+ const categoryDefaults = new Map();
83
+
84
+ async function resolveConflict(conflict) {
85
+ const category = String(conflict.category || "default");
86
+ if (categoryDefaults.has(category)) {
87
+ return categoryDefaults.get(category);
88
+ }
89
+
90
+ const label = String(conflict.label || "未知资产");
91
+ const location = conflict.path ? ` (${conflict.path})` : "";
92
+
93
+ console.log(`\n[confirm] 检测到已存在资产: ${label}${location}`);
94
+ console.log("请选择处理方式:");
95
+ console.log(" k) 保留(跳过此资产)");
96
+ console.log(" b) 备份后移除(推荐)");
97
+ console.log(" r) 直接移除(不备份)");
98
+
99
+ while (true) {
100
+ const answer = String(await askQuestion(rl, "请输入 k / b / r: ")).trim().toLowerCase();
101
+ let action = "";
102
+ if (answer === "k") action = "keep";
103
+ if (answer === "b") action = "backup";
104
+ if (answer === "r") action = "remove";
105
+
106
+ if (!action) {
107
+ console.log("[warn] 输入无效,请重新输入。");
108
+ continue;
109
+ }
110
+
111
+ const applyAnswer = String(await askQuestion(rl, `是否对同类资产(类别: ${category})后续冲突复用该选择? (y/N): `))
112
+ .trim()
113
+ .toLowerCase();
114
+ if (applyAnswer === "y" || applyAnswer === "yes") {
115
+ categoryDefaults.set(category, action);
116
+ }
117
+
118
+ return action;
119
+ }
120
+ }
121
+
122
+ function close() {
123
+ rl.close();
124
+ }
125
+
126
+ return { resolveConflict, close };
127
+ }
128
+
63
129
  module.exports = {
64
- selectTargets
130
+ selectTargets,
131
+ createConflictPrompter,
65
132
  };