dsh-unplug 0.2.4 → 0.3.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/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.0 (2026-08-20)
4
+
5
+ - UX improvement: `fix` alias for `reconcile`(傻子也会用)。
6
+ - `remove --dry-run` — preview what would be removed without executing.
7
+ - Friendlier CLI help with `--dry-run` flag.
8
+
9
+
3
10
  ## 0.2.4 (2026-08-20)
4
11
 
5
12
  - Fix CI: add `prepare` script (doctor requirement for git installs).
package/lib/cli.js CHANGED
@@ -21,12 +21,14 @@ function usage() {
21
21
  ' disable <plugin> 禁用但不删除(移出 bundles,保留依赖) / disable without deleting',
22
22
  ' enable <plugin> 重新启用(加回 bundles,清除 disabled) / re-enable a disabled plugin',
23
23
  ' audit 检测孤立行/悬空 bundle / detect orphaned rows, dangling bundles',
24
- ' reconcile 自动修复悬空与孤立状态 / auto-fix dangling bundles and orphan rows',
24
+ ' reconcile / fix 自动修复悬空与孤立状态 / auto-fix dangling bundles and orphan rows',
25
+ ' remove <p> --dry-run 预览卸载内容 / preview what would be removed',
25
26
  '',
26
27
  '选项 / Options:',
27
28
  ' --profile <name> Profile 名(默认 default) / profile name',
28
29
  ' --home <path> DSH_HOME 路径(默认 env DSH_HOME 或 ~/.dsh) / DSH_HOME path',
29
30
  ' --yes 确认破坏性操作(remove/reconcile 需要) / confirm destructive ops',
31
+ ' --dry-run 只预览不执行(remove/reconcile) / preview without executing',
30
32
  ' --json 输出机器可读 JSON / machine-readable JSON report',
31
33
  ' --help 显示帮助 / show this help',
32
34
  '',
@@ -38,7 +40,7 @@ function usage() {
38
40
  ].join('\n');
39
41
  }
40
42
  function parseArgs(argv) {
41
- const options = { command: '', profile: 'default', home: '', yes: false, json: false };
43
+ const options = { command: '', profile: 'default', home: '', yes: false, dryRun: false, json: false };
42
44
  const positional = [];
43
45
  for (let i = 0; i < argv.length; i++) {
44
46
  const arg = argv[i];
@@ -51,6 +53,9 @@ function parseArgs(argv) {
51
53
  case '--yes':
52
54
  options.yes = true;
53
55
  break;
56
+ case '--dry-run':
57
+ options.dryRun = true;
58
+ break;
54
59
  case '--profile':
55
60
  options.profile = argv[++i];
56
61
  if (options.profile === undefined)
@@ -127,7 +132,7 @@ export async function main(argv) {
127
132
  console.error(usage());
128
133
  return 2;
129
134
  }
130
- const { command, plugin, profile, home, yes, json } = parsed;
135
+ const { command, plugin, profile, home, yes, dryRun, json } = parsed;
131
136
  const resolvedHome = home || dshHome();
132
137
  try {
133
138
  switch (command) {
@@ -142,7 +147,15 @@ export async function main(argv) {
142
147
  return 2;
143
148
  }
144
149
  const result = reconcile(resolvedHome, profile);
145
- return printResult({ ...result, command }, json);
150
+ return printResult({ ...result, command, profile }, json);
151
+ }
152
+ case 'fix': {
153
+ if (!yes) {
154
+ console.error('fix requires --yes to confirm');
155
+ return 2;
156
+ }
157
+ const result = reconcile(resolvedHome, profile);
158
+ return printResult({ ...result, command: 'reconcile', profile }, json);
146
159
  }
147
160
  case 'remove': {
148
161
  if (!yes) {
@@ -153,6 +166,10 @@ export async function main(argv) {
153
166
  console.error('remove requires a plugin name');
154
167
  return 2;
155
168
  }
169
+ if (dryRun) {
170
+ console.log(`[dry-run] would remove "${plugin}" from profile "${profile}" (bundles + patch rows + dependencies)`);
171
+ return 0;
172
+ }
156
173
  const result = removePlugin(resolvedHome, profile, plugin, { force: yes });
157
174
  return printResult({ ...result, command }, json);
158
175
  }
package/lib/index.js CHANGED
@@ -48,7 +48,7 @@ export function apply(ctx, config) {
48
48
  command: {
49
49
  type: 'string',
50
50
  required: true,
51
- enum: ['list', 'remove', 'disable', 'enable', 'audit', 'reconcile'],
51
+ enum: ['list', 'remove', 'disable', 'enable', 'audit', 'reconcile', 'fix'],
52
52
  description: 'Action to perform / 要执行的操作',
53
53
  },
54
54
  plugin: {
@@ -121,6 +121,19 @@ export function apply(ctx, config) {
121
121
  fixedRows: result.fixedRows,
122
122
  };
123
123
  }
124
+ case 'fix': {
125
+ if (!args.force)
126
+ return errorReport('UNPLUG_REQUIRES_FORCE', 'fix requires `force: true` to confirm the cleanup', profile, args.command);
127
+ const result = reconcile(home, profile);
128
+ return {
129
+ schema: result.schema,
130
+ ok: result.ok,
131
+ command: 'reconcile',
132
+ profile: result.profile,
133
+ fixedBundles: result.fixedBundles,
134
+ fixedRows: result.fixedRows,
135
+ };
136
+ }
124
137
  case 'remove': {
125
138
  if (!args.force)
126
139
  return errorReport('UNPLUG_REQUIRES_FORCE', 'Remove requires `force: true` to confirm the destructive operation', profile, args.command);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-unplug",
3
- "version": "0.2.4",
3
+ "version": "0.3.0",
4
4
  "description": "Plug/unplug any DeepSeek Harness plugin cleanly: list every mounted layer, disable/enable without deleting, remove bundles + patch rows + dependencies in one pass, and audit for orphaned/dangling plugin state. Zero runtime dependencies; read-only by default.",
5
5
  "type": "module",
6
6
  "main": "lib/index.js",