@chengzhao-dev/dsh-cpp-workflow 0.6.6 → 0.6.7

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.en.md CHANGED
@@ -19,13 +19,13 @@ The exception material follows the C++ Core Guidelines' Error handling section a
19
19
  Pinned npm install:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.6
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.7
23
23
  ```
24
24
 
25
25
  The matching Git tag:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.6
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.7
29
29
  ```
30
30
 
31
31
  The desktop app owns the `desktop` profile. If the command-line `add` is rejected, add the plugin in the app settings and use the same pinned npm version. A Git tag install uses the prebuilt `lib/` committed in the repository and runs no build on the target machine; the package has no install script that needs approval.
@@ -34,6 +34,8 @@ The desktop app owns the `desktop` profile. If the command-line `add` is rejecte
34
34
 
35
35
  The plugin page's "C++ Workflow" card is the only entry point and manages file-header tags only: `@file` is always the file name, `@brief` is written by default, and `@author`, `@date`, `@copyright`, `@version` stay off and are written only when they hold a non-empty value; `@param` is required for every parameter; `@return` is written by default but may be omitted when the condition is self-evident; `@throws` only when the function throws. The preview follows the switches, saving takes effect on the next skill read, and existing sources are never rewritten. Settings navigation no longer duplicates the form.
36
36
 
37
+ If the page still reports that it is not connected to a configuration form after an upgrade, the window is still running the previous client bundle: restart DeepSeek Harness or select "Reload page" on the plugin page. The page retries the connection while open and switches to the editable form as soon as the host serves it; reinstalling is unnecessary. Discard changes, Restore defaults and Save are separate: discard returns to the last value read from the host, restore fills the draft, and only Save writes it.
38
+
37
39
  ## Workspace gate
38
40
 
39
41
  Only one condition must hold before either skill appears: walking up from the current directory to the Git root, the path contains `.agents/skills/cpp-workflow-project/SKILL.md`.
package/README.md CHANGED
@@ -19,13 +19,13 @@
19
19
  npm 安装钉死版本:
20
20
 
21
21
  ```bash
22
- dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.6
22
+ dsh plugin --profile web add npm:@chengzhao-dev/dsh-cpp-workflow@0.6.7
23
23
  ```
24
24
 
25
25
  同版本的 Git 标签:
26
26
 
27
27
  ```bash
28
- dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.6
28
+ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.7
29
29
  ```
30
30
 
31
31
  桌面应用独占 `desktop` profile。命令行 `add` 被拒绝时,在应用设置里添加插件,地址填同一个 npm 版本。Git 标签安装使用仓库内已构建的 `lib/`,不在目标机器上重新构建;包内也不含需要授权执行的安装脚本。
@@ -34,6 +34,8 @@ dsh plugin --profile web add github:chengzhao-dev/dsh-cpp-workflow#v0.6.6
34
34
 
35
35
  插件页的「C++ 工程流程」是唯一入口,只管文件头标签:`@file` 固定为文件名,`@brief` 默认写出,`@author`、`@date`、`@copyright`、`@version` 默认关闭且只在填了非空值时才写出;有参数就要求 `@param`,有返回值默认要求 `@return`(成立条件自明时可省),会抛才要求 `@throws`。预览随开关即时更新,保存后下一次读取技能即生效,已有源码不会被改写。设置导航不再重复放一份。
36
36
 
37
+ 升级插件后如果这一页仍显示“暂时没有连到配置表单”,说明当前窗口还挂着旧的前端产物:重启 DeepSeek Harness,或在插件页点“重新载入页面”。页面会在打开后自动重试连接,拿到表单就切换为可编辑,不需要重装插件。保存、撤销修改与恢复默认值分三个按钮:撤销只回到最近一次读到的值,恢复默认值只填草稿,点保存才写盘。
38
+
37
39
  ## 工作区门禁
38
40
 
39
41
  一个会话里只需要满足一件事,技能就会出现:从当前目录向上、停在 git 根,沿途有 `.agents/skills/cpp-workflow-project/SKILL.md`。
package/lib/client.js CHANGED
@@ -1,4 +1,4 @@
1
- // 由 scripts/building-client.mjs 生成,请改 src/comment-policy.ts 与 src/client/app.cjs.js。
1
+ // 由 scripts/building-client.mjs 生成,请改 src/comment-policy.ts 与 src/client/*.js。
2
2
  window.__ModuleLoader__.load({
3
3
  id: "@chengzhao-dev/dsh-cpp-workflow",
4
4
  factory: (require) => {
@@ -237,14 +237,18 @@ const STRINGS = {
237
237
  preview: '预览',
238
238
  previewHint: '按当前开关生成,文件名用占位符表示。',
239
239
  save: '保存',
240
- reset: '恢复出厂值',
240
+ reset: '撤销修改',
241
+ defaults: '恢复默认值',
242
+ restored: '已填入内置默认值,点“保存”后生效。',
241
243
  saved: '已保存,下一次读取技能时生效。',
242
- rejected: '保存被拒绝:配置可能已在别处修改,请恢复出厂值后再试。',
244
+ rejected: '保存被拒绝:配置可能已在别处修改。请先撤销修改,再重新编辑。',
243
245
  unsaved: '有未保存的改动',
244
246
  loading: '正在读取配置…',
245
247
  unavailable: '这个部署没有提供这份配置。',
246
248
  unreadable: '当前账户不能修改这份配置。',
247
- readOnly: '当前部署没有把可编辑配置传给这个页面,下面显示的是出厂值。',
249
+ readOnly: '这个页面暂时没有连到配置表单,下面的值是内置默认值。重启 DeepSeek Harness 后重新打开本页即可编辑。',
250
+ reload: '重新载入页面',
251
+ noService: '(configForms 服务未连接)',
248
252
  fixed: '跟随文件名',
249
253
  fixedHint: '值由实际文件决定,不写进配置。',
250
254
  emptyValue: '留空则由文件自己写',
@@ -273,14 +277,18 @@ const STRINGS = {
273
277
  preview: 'Preview',
274
278
  previewHint: 'Generated from the switches above; the file name is a placeholder.',
275
279
  save: 'Save',
276
- reset: 'Restore defaults',
280
+ reset: 'Discard changes',
281
+ defaults: 'Restore defaults',
282
+ restored: 'Built-in defaults are filled in; select Save to apply them.',
277
283
  saved: 'Saved; the next skill read uses it.',
278
- rejected: 'The host rejected the save: the config changed elsewhere. Restore defaults and try again.',
284
+ rejected: 'The host rejected the save: the config changed elsewhere. Discard your changes and edit again.',
279
285
  unsaved: 'Unsaved changes',
280
286
  loading: 'Reading configuration…',
281
287
  unavailable: 'This deployment does not serve this configuration.',
282
288
  unreadable: 'This account cannot edit the configuration.',
283
- readOnly: 'This deployment does not pass an editable form to this page; the values below are defaults.',
289
+ readOnly: 'This page is not connected to a configuration form yet; the values below are built-in defaults. Restart DeepSeek Harness, then reopen this page to edit.',
290
+ reload: 'Reload page',
291
+ noService: ' (the configForms service is not connected)',
284
292
  fixed: 'Follows the file name',
285
293
  fixedHint: 'Derived from the file itself, never stored in the config.',
286
294
  emptyValue: 'Leave empty and each file writes its own',
@@ -357,6 +365,9 @@ const CSS = `
357
365
  .dcw-status{flex:1 1 200px;margin:0;color:var(--dcw-muted);font-size:12px}
358
366
  .dcw-status[data-tone="warn"]{color:var(--dcw-danger)}
359
367
  .dcw-status[data-tone="ok"]{color:var(--dsw-alias-state-success-primary,#1a7f37)}
368
+ .dcw-note-code{font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:11px;opacity:.8;margin-left:6px}
369
+ .dcw-note-action{margin-left:auto;flex:none;padding:3px 10px;border:1px solid currentColor;border-radius:6px;background:transparent;color:inherit;font:inherit;font-size:11.5px;cursor:pointer;white-space:nowrap}
370
+ .dcw-note-action:hover{background:var(--dcw-bg1)}
360
371
  @media (max-width:640px){.dcw-row{grid-template-columns:1fr;gap:4px}.dcw-row-label{padding-top:0}.dcw-flags{grid-template-columns:1fr}.dcw-flag:nth-child(2){border-top:1px solid var(--dcw-border)}}
361
372
  `
362
373
 
@@ -521,10 +532,21 @@ function useFormSnapshot(form) {
521
532
  return snapshot
522
533
  }
523
534
 
535
+ // 配置卡片本身:状态机、保存/撤销/恢复默认值,以及组合包条目里的挂点。
536
+ // 分片按 scripts/building-client.mjs 的模块表顺序拼接:shell → text → styles →
537
+ // widgets → form-state → 本文件。分片共享同一个工厂作用域,不要各自 require。
538
+
539
+ // 宿主组合包 cordis.patch.yml 插入的 profile 条目 id;设置服务按它定位表单。
540
+ const ENTRY_ID = 'cpp-workflow'
541
+ // configForms 可能晚于本插件挂载;页面在首次绘制后短暂重试,拿不到再给手动恢复入口。
542
+ const FORM_RETRY_MS = 500
543
+ const MAX_FORM_RETRIES = 20
544
+
524
545
  function ConfigPage(props) {
525
546
  const s = t()
526
547
  ensureStyles()
527
- const form = props.form
548
+ // form 由挂点注入;resolveForm 兜住“服务晚到”的部署,拿到后下一次绘制自动接上。
549
+ const form = props.form || (typeof props.resolveForm === 'function' ? props.resolveForm() : undefined)
528
550
  const state = useFormSnapshot(form)
529
551
  const initial = React.useMemo(() => normalizeConfig(state.value), [])
530
552
  const [draft, setDraft] = React.useState(initial)
@@ -533,11 +555,13 @@ function ConfigPage(props) {
533
555
  const [busy, setBusy] = React.useState(false)
534
556
  const [message, setMessage] = React.useState('')
535
557
  const [tone, setTone] = React.useState('')
558
+ const [attempt, setAttempt] = React.useState(0)
536
559
  const revision = state.revision
537
560
  // 快照只有 status / writable / revision;旧版本多出来的 mode 字段已不存在,
538
561
  // 再按它判断会把所有可写表单误判成只读。
539
- const ready = Boolean(state.status === 'ready' && state.writable)
562
+ const ready = Boolean(form && state.status === 'ready' && state.writable)
540
563
  const status = state.status
564
+ const missing = !form
541
565
 
542
566
  React.useEffect(() => {
543
567
  if (dirty || state.status !== 'ready') return
@@ -546,6 +570,12 @@ function ConfigPage(props) {
546
570
  setDraft(next)
547
571
  }, [revision, status, dirty, form, state.value])
548
572
 
573
+ React.useEffect(() => {
574
+ if (form || attempt >= MAX_FORM_RETRIES || typeof setTimeout !== 'function') return undefined
575
+ const timer = setTimeout(() => setAttempt((value) => value + 1), FORM_RETRY_MS)
576
+ return () => clearTimeout(timer)
577
+ }, [form, attempt])
578
+
549
579
  const update = (patch) => {
550
580
  setDraft((current) => ({ ...current, ...patch }))
551
581
  setDirty(true)
@@ -553,6 +583,7 @@ function ConfigPage(props) {
553
583
  setTone('')
554
584
  }
555
585
 
586
+ // 撤销:回到最近一次从宿主读到的值,不写盘。
556
587
  const reset = () => {
557
588
  setDraft(base)
558
589
  setDirty(false)
@@ -560,6 +591,20 @@ function ConfigPage(props) {
560
591
  setTone('')
561
592
  }
562
593
 
594
+ // 恢复默认值:只填进草稿,点“保存”后才写盘,避免误触直接覆盖已保存的配置。
595
+ const restoreDefaults = () => {
596
+ setDraft(normalizeConfig(DEFAULT_CONFIG))
597
+ setDirty(true)
598
+ setMessage(s.restored)
599
+ setTone('')
600
+ }
601
+
602
+ const reload = () => {
603
+ if (typeof window !== 'undefined' && window.location && typeof window.location.reload === 'function') {
604
+ window.location.reload()
605
+ }
606
+ }
607
+
563
608
  const save = async () => {
564
609
  if (!ready || busy) return
565
610
  const operations = diffOperations(draft, base)
@@ -573,6 +618,9 @@ function ConfigPage(props) {
573
618
  setMessage(s.saved)
574
619
  setTone('ok')
575
620
  } else {
621
+ // 被拒时宿主已刷新镜像;把基准对齐到新快照,撤销才不会回到过期值。
622
+ const fresh = snapshotOf(form)
623
+ if (fresh && fresh.status === 'ready') setBase(normalizeConfig(fresh.value))
576
624
  setMessage(s.rejected)
577
625
  setTone('warn')
578
626
  }
@@ -597,12 +645,22 @@ function ConfigPage(props) {
597
645
  h('h3', { className: 'dcw-title' }, s.title),
598
646
  h('p', { className: 'dcw-lede' }, s.intro),
599
647
  ),
600
- !editable
648
+ missing
601
649
  ? h(
602
650
  'div',
603
- { className: 'dcw-note', role: 'status' },
651
+ { className: 'dcw-note', role: 'status', 'data-tone': 'warn' },
604
652
  h('span', { className: 'dcw-note-dot' }),
605
- h('span', null, s.readOnly),
653
+ h(
654
+ 'span',
655
+ null,
656
+ s.readOnly,
657
+ props.hasService === false ? h('span', { className: 'dcw-note-code' }, s.noService) : null,
658
+ ),
659
+ h(
660
+ 'button',
661
+ { type: 'button', className: 'dcw-note-action', onClick: reload },
662
+ s.reload,
663
+ ),
606
664
  )
607
665
  : null,
608
666
  editable && !ready && status !== 'missing'
@@ -704,24 +762,31 @@ function ConfigPage(props) {
704
762
  ),
705
763
  h('button', { type: 'button', className: 'dcw-btn', onClick: reset, disabled: busy || !dirty }, s.reset),
706
764
  h(
707
- 'p',
708
- { className: 'dcw-status', role: 'status', 'data-tone': tone },
709
- message || (dirty ? s.unsaved : ''),
765
+ 'button',
766
+ { type: 'button', className: 'dcw-btn', onClick: restoreDefaults, disabled: busy || !ready },
767
+ s.defaults,
710
768
  ),
769
+ h('p', { className: 'dcw-status', role: 'status', 'data-tone': tone }, message || (dirty ? s.unsaved : '')),
711
770
  )
712
771
  : null,
713
772
  )
714
773
  }
715
774
 
716
- // 组合包 cordis.patch.yml 插入的 profile 条目 id;设置服务按它定位表单。
717
- const ENTRY_ID = 'cpp-workflow'
718
-
719
- function page(ownerProps, form) {
720
- return h(ConfigPage, { ...(ownerProps || {}), form })
775
+ function page(ownerProps, form, resolveForm, hasService) {
776
+ return h(ConfigPage, { ...(ownerProps || {}), form, resolveForm, hasService })
721
777
  }
722
778
 
723
779
  function apply(ctx) {
724
- const form = ctx.configForms ? ctx.configForms.get(ENTRY_ID) : undefined
780
+ // 每次取表单都重新查询服务:宿主可能在本插件之后才挂载 configForms。
781
+ const resolveForm = () => {
782
+ if (!ctx.configForms || typeof ctx.configForms.get !== 'function') return undefined
783
+ try {
784
+ return ctx.configForms.get(ENTRY_ID)
785
+ } catch {
786
+ return undefined
787
+ }
788
+ }
789
+ const hasService = Boolean(ctx.configForms && typeof ctx.configForms.get === 'function')
725
790
  const mount = () =>
726
791
  ctx.slots.inject('plugins.bundle.config', () =>
727
792
  ctx.slots.register(
@@ -730,7 +795,10 @@ function apply(ctx) {
730
795
  key: PACKAGE_NAME,
731
796
  label: () => t().nav,
732
797
  },
733
- (ownerProps) => (ownerProps && ownerProps.view === 'summary' ? null : page(ownerProps, form)),
798
+ (ownerProps) =>
799
+ ownerProps && ownerProps.view === 'summary'
800
+ ? null
801
+ : page(ownerProps, undefined, resolveForm, hasService),
734
802
  ),
735
803
  )
736
804
  if (ctx.configForms && typeof ctx.configForms.whileServed === 'function') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chengzhao-dev/dsh-cpp-workflow",
3
- "version": "0.6.6",
3
+ "version": "0.6.7",
4
4
  "description": "写 C++ 源码、配 CMake、用 Clang 构建;头文件用 /// Doxygen 文档注释,函数体用 // 分段说明,文件头标签可在设置里改。 · Write C++ sources, configure CMake, and build with Clang; /// Doxygen on declarations, // section notes in bodies, and configurable file-header tags.",
5
5
  "type": "module",
6
6
  "main": "./lib/index.js",