@lmzhen/dsh-evolution-skill-health 0.32.6 → 0.34.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/lib/client.js CHANGED
@@ -321,12 +321,22 @@ window.__ModuleLoader__.load({
321
321
  "verdict.unreadable": "不可读",
322
322
  "group.proposals": "语义提议",
323
323
  "proposal.unavailable": "提议源未挂载;这不是「没有可拆项」。",
324
+ "proposal.stale": "提议通道的数据已过期;这不是「没有可整理内容」,请刷新后重试。",
325
+ "proposal.owner": "归属",
324
326
  "proposal.outcome.keep": "保持",
325
327
  "proposal.outcome.extract": "提取",
326
328
  "proposal.outcome.mixed": "混合",
327
- "proposal.outcome.owner-gap": "owner 缺口",
328
- "proposal.owner.covered": "有覆盖 owner",
329
- "proposal.owner.owner-gap": "无覆盖 owner",
329
+ "proposal.outcome.owner-gap": "缺少归属技能",
330
+ "proposal.owner.covered": "已有归属技能",
331
+ "proposal.owner.owner-gap": "缺少归属技能",
332
+ "rule.unreadable-skill": "无法读取",
333
+ "rule.body-oversize-2x": "正文超硬线两倍",
334
+ "rule.body-oversize": "正文超过软线",
335
+ "rule.stamp-density": "时间戳密度",
336
+ "rule.body-without-support": "大正文无支撑文件",
337
+ "rule.references-sprawl": "支撑文件过多",
338
+ "rule.scope-overloaded": "范围过载",
339
+ "rule.write-ghost": "只写不读",
330
340
  "proposal.status.proposed": "状态:提议",
331
341
  "proposal.status.previewed": "状态:已预览",
332
342
  "proposal.status.stale": "状态:已过期",
@@ -393,12 +403,22 @@ window.__ModuleLoader__.load({
393
403
  "verdict.unreadable": "Unreadable",
394
404
  "group.proposals": "Semantic proposals",
395
405
  "proposal.unavailable": "No proposal source is mounted; this is not \"nothing to extract\".",
406
+ "proposal.stale": "The proposal lane is stale; refresh and retry. This is not \"nothing to extract\".",
407
+ "proposal.owner": "Owner",
396
408
  "proposal.outcome.keep": "keep",
397
409
  "proposal.outcome.extract": "extract",
398
410
  "proposal.outcome.mixed": "mixed",
399
411
  "proposal.outcome.owner-gap": "owner-gap",
400
- "proposal.owner.covered": "covered owner",
401
- "proposal.owner.owner-gap": "no covering owner",
412
+ "proposal.owner.covered": "owner skill present",
413
+ "proposal.owner.owner-gap": "no owner skill",
414
+ "rule.unreadable-skill": "Unreadable",
415
+ "rule.body-oversize-2x": "Body over hard line ×2",
416
+ "rule.body-oversize": "Body over soft line",
417
+ "rule.stamp-density": "Timestamp density",
418
+ "rule.body-without-support": "Large body, no support files",
419
+ "rule.references-sprawl": "Too many support files",
420
+ "rule.scope-overloaded": "Scope overloaded",
421
+ "rule.write-ghost": "Written, never read",
402
422
  "proposal.status.proposed": "status: proposed",
403
423
  "proposal.status.previewed": "status: previewed",
404
424
  "proposal.status.stale": "status: stale",
@@ -514,6 +534,21 @@ window.__ModuleLoader__.load({
514
534
  ":where(.dsh-evolution-skill-health [data-role=\"note\"]) { margin: 0; color: var(--dsw-alias-label-secondary); }"
515
535
  ].join(String.fromCharCode(10));
516
536
  //#endregion
537
+ //#region src/client/platform-tabs.ts
538
+ const PLATFORM_UI_MODULE = "@deepseek-ai/dsh-client-ui-primitives";
539
+ let cached;
540
+ /** The platform component, or undefined when this deployment has no module row. */
541
+ function platformSegmentedTabs() {
542
+ if (cached !== void 0) return cached ?? void 0;
543
+ try {
544
+ const row = require(PLATFORM_UI_MODULE);
545
+ cached = typeof row.SegmentedTabs === "function" ? row.SegmentedTabs : null;
546
+ } catch {
547
+ cached = null;
548
+ }
549
+ return cached ?? void 0;
550
+ }
551
+ //#endregion
517
552
  //#region src/client/Panel.ts
518
553
  /**
519
554
  * The panel body: a four-state summary plus the list aggregated BY RULE.
@@ -533,6 +568,23 @@ window.__ModuleLoader__.load({
533
568
  "unreadable"
534
569
  ];
535
570
  /** UI tone per verdict; the CSS keys are ok/warn/error/unreadable. */
571
+ const RULE_KEYS = new Set([
572
+ "unreadable-skill",
573
+ "body-oversize-2x",
574
+ "body-oversize",
575
+ "stamp-density",
576
+ "body-without-support",
577
+ "references-sprawl",
578
+ "scope-overloaded",
579
+ "write-ghost"
580
+ ]);
581
+ /** A rule speaks the operator's language; an unknown rule keeps its raw id rather than inventing one. */
582
+ function ruleName(t, rule) {
583
+ if (!RULE_KEYS.has(rule)) return rule;
584
+ const key = "rule." + rule;
585
+ const label = t(key);
586
+ return label === key ? rule : label;
587
+ }
536
588
  const TONE = Object.freeze({
537
589
  healthy: "ok",
538
590
  warn: "warn",
@@ -607,7 +659,7 @@ window.__ModuleLoader__.load({
607
659
  key: row.name,
608
660
  "data-role": "skill-row",
609
661
  "data-tone": row.verdict
610
- }, (0, react.createElement)("span", { "data-role": "skill-name" }, row.name), (0, react.createElement)("span", { "data-role": "skill-verdict" }, t("verdict." + row.verdict)), (0, react.createElement)("span", { "data-role": "skill-findings" }, row.findings.length === 0 ? t("findings.none") : row.findings.map((finding) => finding.rule).join(" / ")), (0, react.createElement)("span", { "data-role": "skill-dims" }, t("dims.body") + ": " + String(row.dims?.bodyChars ?? t("dims.unknown")) + " · " + t("dims.support") + ": " + String(row.dims?.supportFiles ?? t("dims.unknown"))), (0, react.createElement)("span", { "data-role": "skill-row-actions" }, (0, react.createElement)("button", {
662
+ }, (0, react.createElement)("span", { "data-role": "skill-name" }, row.name), (0, react.createElement)("span", { "data-role": "skill-verdict" }, t("verdict." + row.verdict)), (0, react.createElement)("span", { "data-role": "skill-findings" }, row.findings.length === 0 ? t("findings.none") : row.findings.map((finding) => ruleName(t, finding.rule)).join(" / ")), row.verdict === "unreadable" ? (0, react.createElement)("span", { "data-role": "skill-unreadable-note" }, t("unreadable.note")) : null, row.protection === void 0 || row.protection === null ? null : (0, react.createElement)("span", { "data-role": "skill-protection" }, t("protected") + ": " + row.protection), (0, react.createElement)("span", { "data-role": "skill-dims" }, t("dims.body") + ": " + String(row.dims?.bodyChars ?? t("dims.unknown")) + " · " + t("dims.support") + ": " + String(row.dims?.supportFiles ?? t("dims.unknown"))), (0, react.createElement)("span", { "data-role": "skill-row-actions" }, (0, react.createElement)("button", {
611
663
  type: "button",
612
664
  "data-role": "action-history",
613
665
  disabled: face.openHistory === void 0,
@@ -641,7 +693,7 @@ window.__ModuleLoader__.load({
641
693
  key: entry.rule,
642
694
  "data-role": "rule-row",
643
695
  "data-tone": TONE[worstVerdict(face, entry.skills)]
644
- }, cell("rule-name", entry.rule), cell("skills", entry.skills.map((name, i) => (0, react.createElement)("span", {
696
+ }, cell("rule-name", ruleName(t, entry.rule)), cell("skills", entry.skills.map((name, i) => (0, react.createElement)("span", {
645
697
  "data-role": "skill-pill",
646
698
  key: name + ":" + String(i),
647
699
  title: name
@@ -678,14 +730,17 @@ window.__ModuleLoader__.load({
678
730
  const [loading, setLoading] = (0, react.useState)(face.loadRows !== void 0 && face.loaded !== true);
679
731
  const [lastReadAt, setLastReadAt] = (0, react.useState)(face.readAt);
680
732
  const [scan, setScan] = (0, react.useState)(face.scan);
733
+ const [viewMode, setViewMode] = (0, react.useState)("skills");
734
+ const firstLoad = (0, react.useRef)(false);
735
+ const readTicket = (0, react.useRef)(0);
681
736
  /** One read trigger for first paint and the refresh button; the latest answer wins. */
682
737
  const load = () => {
683
738
  const read = face.loadRows;
684
739
  if (read === void 0) return () => {};
685
- let live = true;
740
+ const ticket = ++readTicket.current;
686
741
  setLoading(true);
687
742
  read().then((outcome) => {
688
- if (!live) return;
743
+ if (ticket !== readTicket.current) return;
689
744
  if (outcome.ok) {
690
745
  const payload = outcome.payload;
691
746
  setView({
@@ -701,18 +756,20 @@ window.__ModuleLoader__.load({
701
756
  setFailed(void 0);
702
757
  } else setFailed(outcome.failure);
703
758
  }).catch(() => {
704
- if (live) setFailed({
759
+ if (ticket === readTicket.current) setFailed({
705
760
  kind: "server",
706
761
  status: 0
707
762
  });
708
763
  }).finally(() => {
709
- if (live) setLoading(false);
764
+ if (ticket === readTicket.current) setLoading(false);
710
765
  });
711
- return () => {
712
- live = false;
713
- };
766
+ return () => {};
714
767
  };
715
- (0, react.useEffect)(() => load(), [face]);
768
+ (0, react.useEffect)(() => {
769
+ if (firstLoad.current || face.loadRows === void 0) return;
770
+ firstLoad.current = true;
771
+ load();
772
+ }, [face]);
716
773
  const counts = new Map(KINDS.map((kind) => [kind, view.rows.filter((row) => row.verdict === kind).length]));
717
774
  const stats = (0, react.createElement)("div", { "data-role": "stats" }, KINDS.map((kind) => (0, react.createElement)("div", {
718
775
  key: kind,
@@ -726,26 +783,61 @@ window.__ModuleLoader__.load({
726
783
  onClick: () => {
727
784
  load();
728
785
  }
729
- }, t("refresh"))), (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), loading || failed !== void 0 || scan?.status === "unavailable" ? null : stats, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, view.truncated ? (0, react.createElement)("p", { "data-role": "note" }, t("scan.truncated")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failureBlock(t, failed)), view.loaded === true && view.rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty.noneReported")) : null, view.proposalStatus === "unavailable" && (view.proposals ?? []).length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("proposal.unavailable")) : null, (view.proposals ?? []).length > 0 ? (0, react.createElement)("div", { "data-role": "proposals" }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.proposals")), ...(view.proposals ?? []).map((proposal) => {
786
+ }, t("refresh"))), (0, react.createElement)("p", { "data-role": "hint" }, t("hint")), loading || failed !== void 0 || scan?.status === "unavailable" ? null : stats, loading ? (0, react.createElement)("p", { "data-role": "note" }, t("loading")) : null, view.truncated ? (0, react.createElement)("p", { "data-role": "note" }, t("scan.truncated")) : null, failed === void 0 ? null : (0, react.createElement)("div", { "data-role": "note" }, failureBlock(t, failed)), view.loaded === true && view.rows.length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("empty.noneReported")) : null, view.proposalStatus === "unavailable" && (view.proposals ?? []).length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("proposal.unavailable")) : null, view.proposalStatus === "stale" && (view.proposals ?? []).length === 0 ? (0, react.createElement)("p", { "data-role": "note" }, t("proposal.stale")) : null, (view.proposals ?? []).length > 0 ? (0, react.createElement)("div", { "data-role": "proposals" }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.proposals")), ...(view.proposals ?? []).map((proposal) => {
730
787
  const heading = proposal.parts?.[0]?.heading;
731
788
  const target = proposal.owner.mode === "covered" ? proposal.owner.name : proposal.owner.neededOwner;
732
789
  const command = heading === void 0 || target === void 0 ? void 0 : "/evolution extract " + proposal.source + " " + target + " \"" + heading + "\" --plan";
733
790
  return (0, react.createElement)("div", {
734
791
  key: proposal.id,
735
792
  "data-role": "proposal"
736
- }, (0, react.createElement)("p", { "data-role": "proposal-summary" }, proposal.source + " — " + t("proposal.outcome." + proposal.outcome) + " · " + (proposal.owner.mode === "covered" ? t("proposal.owner.covered") + (proposal.owner.name === void 0 ? "" : ": " + proposal.owner.name) : t("proposal.owner.owner-gap") + (proposal.owner.neededOwner === void 0 ? "" : ": " + proposal.owner.neededOwner))), (0, react.createElement)("p", { "data-role": "proposal-status" }, t("proposal.status." + proposal.status)), proposal.provenance === void 0 ? null : (0, react.createElement)("details", { "data-role": "proposal-provenance" }, (0, react.createElement)("summary", null, t("proposal.provenance")), (0, react.createElement)("p", null, proposal.provenance.promptVersion + (proposal.provenance.model === void 0 ? "" : " · " + proposal.provenance.model) + " · " + proposal.provenance.runId + " · " + new Date(proposal.provenance.at).toLocaleString() + (proposal.provenance.promptHash === void 0 ? "" : " · #" + proposal.provenance.promptHash.slice(0, 8)))), proposal.residual === void 0 || proposal.residual.trim() === "" ? null : (0, react.createElement)("p", { "data-role": "proposal-kept" }, t("proposal.kept") + ": " + proposal.residual.trim().slice(0, 160)), (proposal.parts ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-reusable" }, t("proposal.reusable") + ": " + (proposal.parts ?? []).map((part) => part.heading).join("、")), (proposal.warnings ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-warnings" }, t("proposal.warnings") + ": " + (proposal.warnings ?? []).join(";")), command === void 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-command" }, (0, react.createElement)("code", {}, command), (0, react.createElement)(CopyButton, {
793
+ }, (0, react.createElement)("p", { "data-role": "proposal-summary" }, proposal.source + " — " + t("proposal.outcome." + proposal.outcome)), (0, react.createElement)("p", { "data-role": "proposal-status" }, t("proposal.status." + proposal.status)), (0, react.createElement)("details", { "data-role": "proposal-provenance" }, (0, react.createElement)("summary", null, t("proposal.provenance")), (0, react.createElement)("p", { "data-role": "proposal-owner" }, t("proposal.owner") + ": " + (proposal.owner.mode === "covered" ? t("proposal.owner.covered") + (proposal.owner.name === void 0 ? "" : ": " + proposal.owner.name) : t("proposal.owner.owner-gap") + (proposal.owner.neededOwner === void 0 ? "" : ": " + proposal.owner.neededOwner))), proposal.provenance === void 0 ? null : (0, react.createElement)("p", null, proposal.provenance.promptVersion + (proposal.provenance.model === void 0 ? "" : " · " + proposal.provenance.model) + " · " + proposal.provenance.runId + " · " + new Date(proposal.provenance.at).toLocaleString() + (proposal.provenance.promptHash === void 0 ? "" : " · #" + proposal.provenance.promptHash.slice(0, 8)))), proposal.residual === void 0 || proposal.residual.trim() === "" ? null : (0, react.createElement)("p", { "data-role": "proposal-kept" }, t("proposal.kept") + ": " + proposal.residual.trim().slice(0, 160)), (proposal.parts ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-reusable" }, t("proposal.reusable") + ": " + (proposal.parts ?? []).map((part) => part.heading).join("、")), (proposal.warnings ?? []).length === 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-warnings" }, t("proposal.warnings") + ": " + (proposal.warnings ?? []).join(";")), command === void 0 ? null : (0, react.createElement)("p", { "data-role": "proposal-command" }, (0, react.createElement)("code", {}, command), (0, react.createElement)(CopyButton, {
737
794
  command,
738
795
  t,
739
796
  role: "proposal-copy",
740
797
  label: t("proposal.copy")
741
798
  })));
742
- })) : null, skillQueue(view, t), (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.byRule")), ruleTable(view, t), view.rows.filter((row) => row.verdict === "unreadable").map((row) => (0, react.createElement)("p", {
743
- key: row.name + ":unreadable",
744
- "data-role": "note"
745
- }, row.name + " — " + t("unreadable.note"))), view.rows.filter((row) => row.protection !== void 0 && row.protection !== null).map((row) => (0, react.createElement)("p", {
746
- key: row.name + ":protected",
747
- "data-role": "note"
748
- }, row.name + " — " + t("protected"))));
799
+ })) : null, (() => {
800
+ const Tabs = platformSegmentedTabs();
801
+ const items = [{
802
+ value: "skills",
803
+ label: t("group.bySkill"),
804
+ id: "evo-health-tab-skills",
805
+ panelId: "evo-health-skills"
806
+ }, {
807
+ value: "rules",
808
+ label: t("group.byRule"),
809
+ id: "evo-health-tab-rules",
810
+ panelId: "evo-health-rules"
811
+ }];
812
+ if (Tabs !== void 0) return (0, react.createElement)(Tabs, {
813
+ items,
814
+ value: viewMode,
815
+ onChange: (next) => {
816
+ setViewMode(next === "rules" ? "rules" : "skills");
817
+ },
818
+ label: t("group.byRule")
819
+ });
820
+ return (0, react.createElement)("div", {
821
+ role: "tablist",
822
+ "data-role": "view-tabs"
823
+ }, items.map((item) => (0, react.createElement)("button", {
824
+ key: item.value,
825
+ type: "button",
826
+ role: "tab",
827
+ "aria-selected": viewMode === item.value ? "true" : "false",
828
+ "aria-controls": item.panelId,
829
+ id: item.id,
830
+ onClick: () => {
831
+ setViewMode(item.value === "rules" ? "rules" : "skills");
832
+ }
833
+ }, item.label)));
834
+ })(), viewMode === "skills" ? (0, react.createElement)("div", {
835
+ id: "evo-health-skills",
836
+ role: "tabpanel"
837
+ }, skillQueue(view, t)) : (0, react.createElement)("div", {
838
+ id: "evo-health-rules",
839
+ role: "tabpanel"
840
+ }, (0, react.createElement)("h3", { "data-role": "group-title" }, t("group.byRule")), ruleTable(view, t)));
749
841
  }
750
842
  //#endregion
751
843
  //#region src/client/seats.ts
@@ -753,24 +845,17 @@ window.__ModuleLoader__.load({
753
845
  function requireHealthSeats(ctx) {
754
846
  if (ctx.slots === void 0) throw new Error("skill-health: the client slots seat is not mounted");
755
847
  }
756
- /** The one place the panel crosses into the platform layout seat. */
757
- function openSkillHistory(ctx, name) {
758
- ctx.layout.openPage("evolution.workbench", name === void 0 ? { view: "history" } : {
759
- view: "history",
760
- skillName: name
761
- });
762
- }
763
848
  //#endregion
764
849
  //#region src/client/index.ts
765
850
  /** Required client services: the slot registry, locale seat and page navigation. */
766
- const inject = [
767
- "slots",
768
- "locale",
769
- "layout"
770
- ];
851
+ const inject = ["slots", "locale"];
771
852
  /** The view component receives the workbench's activeView marker alongside its face. */
772
853
  function HealthView(props) {
773
- return props.activeView === "health" ? SkillHealthPanel(props) : null;
854
+ if (props.activeView !== "health") return null;
855
+ return props.openHistory === void 0 ? SkillHealthPanel(props) : SkillHealthPanel({
856
+ ...props,
857
+ openHistory: props.openHistory
858
+ });
774
859
  }
775
860
  /**
776
861
  * Register the health view on the workbench page.
@@ -798,9 +883,6 @@ window.__ModuleLoader__.load({
798
883
  loaded: false,
799
884
  proposalStatus: "unavailable",
800
885
  loadRows: rows,
801
- openHistory: (name) => {
802
- openSkillHistory(ctx, name);
803
- },
804
886
  t
805
887
  })
806
888
  });
@@ -0,0 +1,17 @@
1
+ /** Platform SegmentedTabs reached through the rc.3 client module table. */
2
+ import type { ReactNode } from 'react';
3
+ export interface SegmentedTabItem {
4
+ readonly value: string;
5
+ readonly label: ReactNode;
6
+ readonly id: string;
7
+ readonly panelId: string;
8
+ }
9
+ export type SegmentedTabsComponent = (props: {
10
+ readonly items: readonly SegmentedTabItem[];
11
+ readonly value: string;
12
+ readonly onChange: (value: string) => void;
13
+ readonly label: string;
14
+ }) => ReactNode;
15
+ /** The platform component, or undefined when this deployment has no module row. */
16
+ export declare function platformSegmentedTabs(): SegmentedTabsComponent | undefined;
17
+ //# sourceMappingURL=platform-tabs.d.ts.map
@@ -1,6 +1,4 @@
1
1
  import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
2
  /** Fail loud when the injected client seats are absent, instead of leaving a PENDING row. */
3
3
  export declare function requireHealthSeats(ctx: ClientContext): void;
4
- /** The one place the panel crosses into the platform layout seat. */
5
- export declare function openSkillHistory(ctx: ClientContext, name?: string): void;
6
4
  //# sourceMappingURL=seats.d.ts.map
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  ]
12
12
  }
13
13
  },
14
- "version": "0.32.6",
14
+ "version": "0.34.0",
15
15
  "publishConfig": {
16
16
  "access": "public"
17
17
  },
@@ -42,18 +42,18 @@
42
42
  "peerDependencies": {
43
43
  "@deepseek-ai/cordis": "^4.0.5",
44
44
  "react": "^18.2.0",
45
- "@lmzhen/dsh-evolution-core": "^0.32.6",
46
- "@lmzhen/dsh-evolution-curator": "^0.32.6"
45
+ "@lmzhen/dsh-evolution-core": "^0.34.0",
46
+ "@lmzhen/dsh-evolution-curator": "^0.34.0"
47
47
  },
48
48
  "devDependencies": {
49
49
  "@deepseek-ai/cordis": "^4.0.5",
50
50
  "@types/react": "~18.3.1",
51
51
  "react": "^18.2.0",
52
- "@lmzhen/dsh-evolution-core": "^0.32.6",
53
- "@lmzhen/dsh-evolution-curator": "^0.32.6"
52
+ "@lmzhen/dsh-evolution-core": "^0.34.0",
53
+ "@lmzhen/dsh-evolution-curator": "^0.34.0"
54
54
  },
55
55
  "dependencies": {
56
- "@lmzhen/dsh-evolution-client-kit": "^0.32.6"
56
+ "@lmzhen/dsh-evolution-client-kit": "^0.34.0"
57
57
  },
58
58
  "engines": {
59
59
  "dsh": ">=0.2.1-rc.2 <0.2.2-0"