@wenaixi/dsh-superpower 7.6.0 → 7.7.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 +2 -1
- package/README.zh.md +2 -1
- package/lib/client.js +220 -108
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -195,5 +195,6 @@ Issues and PRs welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md).
|
|
|
195
195
|
|
|
196
196
|
## Credits
|
|
197
197
|
|
|
198
|
-
- Upstream [obra/superpowers](https://github.com/obra/superpowers)
|
|
198
|
+
- Upstream [obra/superpowers](https://github.com/obra/superpowers)
|
|
199
199
|
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) for the three-role plugin architecture
|
|
200
|
+
- [dsh-plugin-dev](https://github.com/Wenaixi/dsh-plugin-dev) for DSH plugin development standards, architecture references, and verification methodologies
|
package/README.zh.md
CHANGED
|
@@ -198,5 +198,6 @@ Issue 和 PR 都欢迎。详见 [CONTRIBUTING.md](./CONTRIBUTING.md)。
|
|
|
198
198
|
|
|
199
199
|
## 致谢
|
|
200
200
|
|
|
201
|
-
- 上游 [obra/superpowers](https://github.com/obra/superpowers)
|
|
201
|
+
- 上游 [obra/superpowers](https://github.com/obra/superpowers)
|
|
202
202
|
- [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) 的三角色插件架构
|
|
203
|
+
- [dsh-plugin-dev](https://github.com/Wenaixi/dsh-plugin-dev) 提供的 DSH 插件开发规范、架构参考与真机验收工程方法
|
package/lib/client.js
CHANGED
|
@@ -31,6 +31,7 @@ window.__ModuleLoader__.load({
|
|
|
31
31
|
var PACKAGE_NAME = '@wenaixi/dsh-superpower';
|
|
32
32
|
var NS = 'dsh-superpower';
|
|
33
33
|
var DISABLED_FIELD = 'disabled';
|
|
34
|
+
var LANGUAGE_FIELD = 'language';
|
|
34
35
|
/* v7.3.0 及之前的双侧禁言表。面板写入成功后会把它们 unset,
|
|
35
36
|
这里仍需列出:迁移那批操作的路径必须与 schema 里的字段名逐字一致。 */
|
|
36
37
|
var LEGACY_FIELDS = ['modelDisabled', 'userDisabled'];
|
|
@@ -147,17 +148,6 @@ window.__ModuleLoader__.load({
|
|
|
147
148
|
return !!dictionary && dictionary[name] === true;
|
|
148
149
|
}
|
|
149
150
|
|
|
150
|
-
/**
|
|
151
|
-
* 组装一次写入的全部操作:主写入 + 两条清理旧表的 unset。
|
|
152
|
-
*
|
|
153
|
-
* 清理动作与主写合在同一次 mutate 里,因为 ConfigForm 的 mutate 是一批
|
|
154
|
-
* 操作共享一个 revision 栅栏、一次校验、一次持久化决策。这样不存在
|
|
155
|
-
* 「新表写了一部分、旧表还留着」的中间态,也省掉每技能一次额外往返。
|
|
156
|
-
* op 为 enable 时 unset 路径即恢复继承的默认值。
|
|
157
|
-
*/
|
|
158
|
-
/* 语言偏好字段:'zh' | 'en' 显式固定;缺失时跟随宿主界面语言(locale active)。 */
|
|
159
|
-
var LANGUAGE_FIELD = 'language';
|
|
160
|
-
|
|
161
151
|
/**
|
|
162
152
|
* 读取当前语言偏好(三态):'zh'/'en' 显式值原样;缺项为 undefined(跟随宿主)。
|
|
163
153
|
* 与宿主侧 readSwitches 同规则,缺失绝不折叠成 zh —— 否则面板无法表达「跟随宿主」。
|
|
@@ -167,86 +157,230 @@ window.__ModuleLoader__.load({
|
|
|
167
157
|
return raw === 'zh' || raw === 'en' ? raw : undefined;
|
|
168
158
|
}
|
|
169
159
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
160
|
+
/**
|
|
161
|
+
* 客户端配置控制器深度模块 (Deep Module):
|
|
162
|
+
* 将 DSH ConfigForm 订阅、批处理 ops 拍平、旧表原子继承、disposed 拦截与堆栈归集彻底封闭在门后。
|
|
163
|
+
*/
|
|
164
|
+
class SwitchPanelModel {
|
|
165
|
+
constructor(scope, options) {
|
|
166
|
+
this.scope = scope;
|
|
167
|
+
this.t = (options && options.t) || ((k) => k);
|
|
168
|
+
this.allNames = (options && options.allNames) || [];
|
|
169
|
+
this.listeners = new Set();
|
|
170
|
+
this.pending = {};
|
|
171
|
+
this.failures = {};
|
|
172
|
+
this.unsubscribe = null;
|
|
173
|
+
this._bindScope(scope);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
_bindScope(scope) {
|
|
177
|
+
if (this.scope !== scope) {
|
|
178
|
+
if (this.unsubscribe) {
|
|
179
|
+
this.unsubscribe();
|
|
180
|
+
this.unsubscribe = null;
|
|
177
181
|
}
|
|
182
|
+
this.scope = scope;
|
|
183
|
+
}
|
|
184
|
+
if (this.scope && typeof this.scope.subscribe === 'function' && !this.unsubscribe) {
|
|
185
|
+
this.unsubscribe = this.scope.subscribe(() => this._notify());
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
updateScope(nextScope) {
|
|
190
|
+
this._bindScope(nextScope);
|
|
191
|
+
this._notify();
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
subscribe(listener) {
|
|
195
|
+
this.listeners.add(listener);
|
|
196
|
+
return () => this.listeners.delete(listener);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
_notify() {
|
|
200
|
+
this.listeners.forEach((fn) => {
|
|
201
|
+
try { fn(); } catch (e) {}
|
|
178
202
|
});
|
|
179
203
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
204
|
+
|
|
205
|
+
getSnapshot() {
|
|
206
|
+
var snap = (this.scope && typeof this.scope.getSnapshot === 'function')
|
|
207
|
+
? this.scope.getSnapshot()
|
|
208
|
+
: { status: 'loading', writable: false, value: {} };
|
|
209
|
+
var value = snap.value || {};
|
|
210
|
+
var rawDisabled = value[DISABLED_FIELD] || {};
|
|
211
|
+
var legacyActive = Object.keys(rawDisabled).length === 0;
|
|
212
|
+
var disabled = !legacyActive
|
|
213
|
+
? rawDisabled
|
|
214
|
+
: Object.assign({}, value.modelDisabled || {}, value.userDisabled || {});
|
|
215
|
+
var language = readLanguage(value);
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
status: snap.status || 'ready',
|
|
219
|
+
writable: snap.writable !== false && snap.mode !== 'memory',
|
|
220
|
+
disabled: disabled,
|
|
221
|
+
legacyActive: legacyActive,
|
|
222
|
+
language: language,
|
|
223
|
+
langConfigured: language !== undefined,
|
|
224
|
+
langValue: language !== undefined ? language : 'auto',
|
|
225
|
+
pending: this.pending,
|
|
226
|
+
failures: this.failures,
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
_buildOps(names, op, legacyCarry) {
|
|
231
|
+
var list = Array.isArray(names) ? names : [names];
|
|
232
|
+
var ops = [];
|
|
233
|
+
if (legacyCarry) {
|
|
234
|
+
Object.keys(legacyCarry).forEach(function (key) {
|
|
235
|
+
if (legacyCarry[key] === true && list.indexOf(key) < 0) {
|
|
236
|
+
ops.push({ op: 'set', path: [DISABLED_FIELD, key], value: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
185
239
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
240
|
+
list.forEach(function (name) {
|
|
241
|
+
if (op === 'disable') {
|
|
242
|
+
ops.push({ op: 'set', path: [DISABLED_FIELD, name], value: true });
|
|
243
|
+
} else {
|
|
244
|
+
ops.push({ op: 'unset', path: [DISABLED_FIELD, name] });
|
|
245
|
+
}
|
|
246
|
+
});
|
|
247
|
+
return ops.concat(LEGACY_FIELDS.map(function (field) {
|
|
248
|
+
return { op: 'unset', path: [field] };
|
|
249
|
+
}));
|
|
250
|
+
}
|
|
191
251
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
252
|
+
async _submit(names, ops) {
|
|
253
|
+
if (!this.scope || this.scope.disposed || typeof this.scope.mutate !== 'function') {
|
|
254
|
+
var err = new Error(this.t('hostUnavailable'));
|
|
255
|
+
console.error('[dsh-superpower] form.mutate failed:', err);
|
|
256
|
+
this._setFailures(names, this.t('writeFailed') + ': ' + err.message);
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
this._setPending(names, true);
|
|
261
|
+
this._clearFailures(names);
|
|
262
|
+
|
|
263
|
+
try {
|
|
264
|
+
var accepted = await this.scope.mutate(ops);
|
|
265
|
+
if (accepted === false) {
|
|
266
|
+
throw new Error(this.t('writeFailed'));
|
|
267
|
+
}
|
|
268
|
+
this._clearFailures(names);
|
|
269
|
+
return true;
|
|
270
|
+
} catch (error) {
|
|
271
|
+
console.error('[dsh-superpower] form.mutate failed:', error);
|
|
272
|
+
var msg = this.t('writeFailed') + ': ' + String(error && error.message ? error.message : error);
|
|
273
|
+
this._setFailures(names, msg);
|
|
274
|
+
return false;
|
|
275
|
+
} finally {
|
|
276
|
+
this._setPending(names, false);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
_setPending(names, isPending) {
|
|
281
|
+
var next = Object.assign({}, this.pending);
|
|
282
|
+
names.forEach(function (name) {
|
|
283
|
+
if (isPending) next[name] = true;
|
|
284
|
+
else delete next[name];
|
|
202
285
|
});
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
return next;
|
|
286
|
+
this.pending = next;
|
|
287
|
+
this._notify();
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
_setFailures(names, message) {
|
|
291
|
+
var next = Object.assign({}, this.failures);
|
|
292
|
+
names.forEach(function (name) { next[name] = message; });
|
|
293
|
+
this.failures = next;
|
|
294
|
+
this._notify();
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_clearFailures(names) {
|
|
298
|
+
var next = Object.assign({}, this.failures);
|
|
299
|
+
var changed = false;
|
|
300
|
+
names.forEach(function (name) {
|
|
301
|
+
if (next[name]) {
|
|
302
|
+
delete next[name];
|
|
303
|
+
changed = true;
|
|
304
|
+
}
|
|
223
305
|
});
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
306
|
+
if (changed) {
|
|
307
|
+
this.failures = next;
|
|
308
|
+
this._notify();
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
toggle(name, nextState) {
|
|
313
|
+
var snap = this.getSnapshot();
|
|
314
|
+
var op = nextState ? 'enable' : 'disable';
|
|
315
|
+
var legacyCarry = snap.legacyActive ? snap.disabled : null;
|
|
316
|
+
var ops = this._buildOps([name], op, legacyCarry);
|
|
317
|
+
return this._submit([name], ops);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
setLanguage(next) {
|
|
321
|
+
var ops = next === 'auto'
|
|
322
|
+
? [{ op: 'unset', path: [LANGUAGE_FIELD] }]
|
|
323
|
+
: [{ op: 'set', path: [LANGUAGE_FIELD], value: next }];
|
|
324
|
+
return this._submit(['language'], ops);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
enableAll() {
|
|
328
|
+
var ops = this._buildOps(this.allNames, 'enable');
|
|
329
|
+
return this._submit(this.allNames, ops);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
disableAll() {
|
|
333
|
+
var ops = this._buildOps(this.allNames, 'disable');
|
|
334
|
+
return this._submit(this.allNames, ops);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
resetAll() {
|
|
338
|
+
// Q2 裁决:恢复默认纯粹重置技能禁言表,保持语言偏好正交独立
|
|
339
|
+
return this.enableAll();
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
dispose() {
|
|
343
|
+
if (this.unsubscribe) {
|
|
344
|
+
this.unsubscribe();
|
|
345
|
+
this.unsubscribe = null;
|
|
346
|
+
}
|
|
347
|
+
this.listeners.clear();
|
|
348
|
+
}
|
|
227
349
|
}
|
|
228
350
|
|
|
229
351
|
/**
|
|
230
|
-
*
|
|
352
|
+
* 技能开关面板 (View)。纯渲染视图,完全由 SwitchPanelModel 控制器驱动。
|
|
231
353
|
*
|
|
232
|
-
* props.
|
|
233
|
-
* props.actions 是本面板独占的写操作面。
|
|
354
|
+
* props.actions.form 是本面板绑定的 ConfigForm 控制器。
|
|
234
355
|
*/
|
|
235
356
|
function SkillSwitchPanel(props) {
|
|
236
357
|
var t = props.t;
|
|
237
358
|
var form = props.actions.form;
|
|
238
|
-
var state = props.useSwitches(function (value) { return value; });
|
|
239
359
|
var hostActive = props.useLocaleActive(function (v) { return v && v.preference === 'en' ? 'en' : 'zh'; });
|
|
360
|
+
var allNames = SKILL_CATALOG.map(function (skill) { return skill.name; });
|
|
361
|
+
|
|
362
|
+
var modelRef = React.useRef(null);
|
|
363
|
+
if (!modelRef.current) {
|
|
364
|
+
modelRef.current = new SwitchPanelModel(form, { t: t, allNames: allNames });
|
|
365
|
+
} else {
|
|
366
|
+
modelRef.current.updateScope(form);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
React.useEffect(() => {
|
|
370
|
+
return () => {
|
|
371
|
+
if (modelRef.current) modelRef.current.dispose();
|
|
372
|
+
};
|
|
373
|
+
}, []);
|
|
374
|
+
|
|
375
|
+
var state = React.useSyncExternalStore(
|
|
376
|
+
(listener) => modelRef.current.subscribe(listener),
|
|
377
|
+
() => modelRef.current.getSnapshot()
|
|
378
|
+
);
|
|
379
|
+
var controller = modelRef.current;
|
|
240
380
|
|
|
241
381
|
var keywordState = React.useState('');
|
|
242
382
|
var keyword = keywordState[0];
|
|
243
383
|
var setKeyword = keywordState[1];
|
|
244
|
-
var pendingState = React.useState({});
|
|
245
|
-
var pending = pendingState[0];
|
|
246
|
-
var setPending = pendingState[1];
|
|
247
|
-
var failuresState = React.useState({});
|
|
248
|
-
var failures = failuresState[0];
|
|
249
|
-
var setFailures = failuresState[1];
|
|
250
384
|
|
|
251
385
|
if (state.status !== 'ready') {
|
|
252
386
|
return h('div', { className: 'spSw' },
|
|
@@ -254,33 +388,18 @@ window.__ModuleLoader__.load({
|
|
|
254
388
|
h('p', { className: 'spSwIntro' }, t('hostUnavailable')));
|
|
255
389
|
}
|
|
256
390
|
|
|
257
|
-
var
|
|
258
|
-
var
|
|
259
|
-
var
|
|
260
|
-
var
|
|
261
|
-
? rawDisabled
|
|
262
|
-
: Object.assign({}, value.modelDisabled || {}, value.userDisabled || {});
|
|
263
|
-
var language = readLanguage(value);
|
|
264
|
-
var langConfigured = language !== undefined;
|
|
265
|
-
var langValue = langConfigured ? language : 'auto';
|
|
391
|
+
var disabled = state.disabled;
|
|
392
|
+
var language = state.language;
|
|
393
|
+
var langConfigured = state.langConfigured;
|
|
394
|
+
var langValue = state.langValue;
|
|
266
395
|
var langOptions = [
|
|
267
396
|
{ value: 'zh', label: t('langZh') },
|
|
268
397
|
{ value: 'en', label: t('langEn') },
|
|
269
398
|
{ value: 'auto', label: t('langAuto') },
|
|
270
399
|
];
|
|
271
|
-
var
|
|
272
|
-
if (next === 'auto') {
|
|
273
|
-
submit(form, t, ['language'], 'enable', setPending, setFailures,
|
|
274
|
-
[{ op: 'unset', path: [LANGUAGE_FIELD] }]);
|
|
275
|
-
} else {
|
|
276
|
-
submit(form, t, ['language'], 'enable', setPending, setFailures,
|
|
277
|
-
[{ op: 'set', path: [LANGUAGE_FIELD], value: next }]);
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
var writable = state.writable !== false && state.mode !== 'memory';
|
|
400
|
+
var writable = state.writable;
|
|
281
401
|
|
|
282
402
|
var lower = keyword.trim().toLowerCase();
|
|
283
|
-
/* 描述取词:按当前语言偏好取对应语言文本,两侧都参与搜索过滤。 */
|
|
284
403
|
var skillText = function (skill) {
|
|
285
404
|
var effective = language === undefined ? hostActive : language;
|
|
286
405
|
return effective === 'en' && skill.descriptionEn ? skill.descriptionEn : skill.description;
|
|
@@ -294,17 +413,9 @@ window.__ModuleLoader__.load({
|
|
|
294
413
|
|| (skill.descriptionEn || '').toLowerCase().indexOf(lower) >= 0;
|
|
295
414
|
});
|
|
296
415
|
|
|
297
|
-
var allNames = SKILL_CATALOG.map(function (skill) { return skill.name; });
|
|
298
|
-
var batchEnable = function () {
|
|
299
|
-
submit(form, t, allNames, 'enable', setPending, setFailures);
|
|
300
|
-
};
|
|
301
|
-
var batchDisable = function () {
|
|
302
|
-
submit(form, t, allNames, 'disable', setPending, setFailures);
|
|
303
|
-
};
|
|
304
|
-
|
|
305
416
|
var rows = shown.map(function (skill) {
|
|
306
|
-
var busy = !!pending[skill.name] || !writable;
|
|
307
|
-
var failure = failures[skill.name];
|
|
417
|
+
var busy = !!state.pending[skill.name] || !writable;
|
|
418
|
+
var failure = state.failures[skill.name];
|
|
308
419
|
var on = !isBlocked(disabled, skill.name);
|
|
309
420
|
return h('li', { key: skill.name, className: 'spSwItem' },
|
|
310
421
|
h('div', { className: 'spSwMain' },
|
|
@@ -319,7 +430,7 @@ window.__ModuleLoader__.load({
|
|
|
319
430
|
label: t('invocable') + ' ' + skill.name,
|
|
320
431
|
title: t('invocableHint'),
|
|
321
432
|
onChange: function (next) {
|
|
322
|
-
|
|
433
|
+
controller.toggle(skill.name, next);
|
|
323
434
|
},
|
|
324
435
|
})));
|
|
325
436
|
});
|
|
@@ -334,13 +445,13 @@ window.__ModuleLoader__.load({
|
|
|
334
445
|
label: t('langTitle'),
|
|
335
446
|
value: langValue,
|
|
336
447
|
options: langOptions,
|
|
337
|
-
disabled: !writable || !!pending['language'],
|
|
338
|
-
onChange:
|
|
448
|
+
disabled: !writable || !!state.pending['language'],
|
|
449
|
+
onChange: function (next) { controller.setLanguage(next); },
|
|
339
450
|
}),
|
|
340
451
|
!langConfigured
|
|
341
452
|
? h('p', { className: 'spSwLangNote' }, t('langUnsetHint', { lang: t(hostActive === 'en' ? 'langEn' : 'langZh') }))
|
|
342
453
|
: h('p', { className: 'spSwLangNote' }, t('langNote')),
|
|
343
|
-
failures['language'] ? h('span', { className: 'spSwLangFailure', role: 'alert' }, failures['language']) : null),
|
|
454
|
+
state.failures['language'] ? h('span', { className: 'spSwLangFailure', role: 'alert' }, state.failures['language']) : null),
|
|
344
455
|
|
|
345
456
|
h('div', { className: 'spSwBar' },
|
|
346
457
|
h(P.Button, {
|
|
@@ -348,21 +459,21 @@ window.__ModuleLoader__.load({
|
|
|
348
459
|
size: 'sm',
|
|
349
460
|
disabled: !writable,
|
|
350
461
|
title: t('resetHint'),
|
|
351
|
-
onClick:
|
|
462
|
+
onClick: function () { controller.enableAll(); },
|
|
352
463
|
}, t('enableAll')),
|
|
353
464
|
h(P.Button, {
|
|
354
465
|
variant: 'outline',
|
|
355
466
|
size: 'sm',
|
|
356
467
|
disabled: !writable,
|
|
357
468
|
title: t('disableAllHint'),
|
|
358
|
-
onClick:
|
|
469
|
+
onClick: function () { controller.disableAll(); },
|
|
359
470
|
}, t('disableAll')),
|
|
360
471
|
h(P.Button, {
|
|
361
472
|
variant: 'ghost',
|
|
362
473
|
size: 'sm',
|
|
363
474
|
disabled: !writable,
|
|
364
475
|
title: t('resetHint'),
|
|
365
|
-
onClick:
|
|
476
|
+
onClick: function () { controller.resetAll(); },
|
|
366
477
|
}, t('resetAll')),
|
|
367
478
|
h(P.Input, {
|
|
368
479
|
className: 'spSwSearch',
|
|
@@ -421,6 +532,7 @@ window.__ModuleLoader__.load({
|
|
|
421
532
|
|
|
422
533
|
exports.apply = apply;
|
|
423
534
|
exports.inject = ['slots', 'configForms', 'locale'];
|
|
535
|
+
exports.SwitchPanelModel = SwitchPanelModel;
|
|
424
536
|
return module.exports;
|
|
425
537
|
},
|
|
426
538
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wenaixi/dsh-superpower",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.7.0",
|
|
4
4
|
"description": "DeepSeek Harness plugin: 15 obra/superpowers engineering skills, bilingual descriptions, per-skill toggles | DeepSeek Harness 插件:15 个 obra/superpowers 工程纪律技能,技能描述中英双语自由切换,每一个技能本身自由开关",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "lib/superpowers.js",
|
|
@@ -21,10 +21,11 @@
|
|
|
21
21
|
"scripts": {
|
|
22
22
|
"build": "tsc -p tsconfig.build.json && node scripts/build-client.mjs",
|
|
23
23
|
"typecheck": "tsc --noEmit",
|
|
24
|
+
"test": "node --test scripts/*.test.mjs",
|
|
24
25
|
"lint": "eslint src --ext .ts 2>&1 || echo \"(no eslint config — skipped)\"",
|
|
25
26
|
"prepare": "npm run build",
|
|
26
27
|
"prepack": "npm run build",
|
|
27
|
-
"verify": "node scripts/verify.mjs && node scripts/check-skill-switches.mjs"
|
|
28
|
+
"verify": "pnpm test && node scripts/verify.mjs && node scripts/check-skill-switches.mjs"
|
|
28
29
|
},
|
|
29
30
|
"dsh": {
|
|
30
31
|
"bundle": {
|