@godv61/dsh-task-engine 0.13.0 → 0.16.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
@@ -4104,12 +4104,14 @@ var configSchema = external_exports.object({
4104
4104
  var viewSchema = external_exports.object({
4105
4105
  ok: external_exports.boolean(),
4106
4106
  source: external_exports.enum(["default", "project", "invalid"]),
4107
+ flow: external_exports.string(),
4107
4108
  config: configSchema,
4108
4109
  problems: external_exports.array(external_exports.string())
4109
4110
  });
4110
4111
  var writeRequestSchema = external_exports.object({
4111
4112
  path: external_exports.string(),
4112
- config: configSchema
4113
+ flow: external_exports.string(),
4114
+ stage_bindings: external_exports.record(external_exports.string(), stageBindingSchema).optional()
4113
4115
  });
4114
4116
  var skillCatalogSchema = external_exports.object({
4115
4117
  skills: external_exports.array(external_exports.object({
@@ -4146,7 +4148,7 @@ var writeResourceResultSchema = external_exports.object({
4146
4148
  });
4147
4149
  var readSkillRequestSchema = external_exports.object({
4148
4150
  name: external_exports.string(),
4149
- level: external_exports.enum(["project", "user"]),
4151
+ level: external_exports.enum(["project", "user", "bundled"]),
4150
4152
  path: external_exports.string().optional()
4151
4153
  });
4152
4154
  var readSkillResultSchema = external_exports.object({
@@ -4159,7 +4161,7 @@ var readSkillResultSchema = external_exports.object({
4159
4161
  });
4160
4162
  var readRuleRequestSchema = external_exports.object({
4161
4163
  name: external_exports.string(),
4162
- level: external_exports.enum(["project", "user"]),
4164
+ level: external_exports.enum(["project", "user", "bundled"]),
4163
4165
  path: external_exports.string().optional()
4164
4166
  });
4165
4167
  var readRuleResultSchema = external_exports.object({
@@ -4168,6 +4170,16 @@ var readRuleResultSchema = external_exports.object({
4168
4170
  content: external_exports.string(),
4169
4171
  error: external_exports.string().optional()
4170
4172
  });
4173
+ var deleteSkillRequestSchema = external_exports.object({
4174
+ name: external_exports.string(),
4175
+ level: external_exports.enum(["project", "user"]),
4176
+ path: external_exports.string().optional()
4177
+ });
4178
+ var deleteRuleRequestSchema = external_exports.object({
4179
+ name: external_exports.string(),
4180
+ level: external_exports.enum(["project", "user"]),
4181
+ path: external_exports.string().optional()
4182
+ });
4171
4183
  var taskLedgerItemSchema = external_exports.object({
4172
4184
  id: external_exports.string(),
4173
4185
  title: external_exports.string(),
@@ -4319,6 +4331,38 @@ var TYPERT_REMOTE = {
4319
4331
  ],
4320
4332
  result: { mode: "strict", typeSymbol: `${PACKAGE}/types#ReadRuleResult`, schema: readRuleResultSchema }
4321
4333
  },
4334
+ {
4335
+ id: `${PACKAGE}#task-engine/deleteSkill`,
4336
+ service: "taskEngineController",
4337
+ namespace: "task-engine",
4338
+ method: "deleteSkill",
4339
+ invocation: { kind: "direct" },
4340
+ parameters: [
4341
+ {
4342
+ name: "request",
4343
+ wire: "request",
4344
+ source: "json",
4345
+ codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#DeleteSkillRequest`, schema: deleteSkillRequestSchema }
4346
+ }
4347
+ ],
4348
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
4349
+ },
4350
+ {
4351
+ id: `${PACKAGE}#task-engine/deleteRule`,
4352
+ service: "taskEngineController",
4353
+ namespace: "task-engine",
4354
+ method: "deleteRule",
4355
+ invocation: { kind: "direct" },
4356
+ parameters: [
4357
+ {
4358
+ name: "request",
4359
+ wire: "request",
4360
+ source: "json",
4361
+ codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#DeleteRuleRequest`, schema: deleteRuleRequestSchema }
4362
+ }
4363
+ ],
4364
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
4365
+ },
4322
4366
  {
4323
4367
  id: `${PACKAGE}#task-engine/readTasks`,
4324
4368
  service: "taskEngineController",
@@ -4341,7 +4385,7 @@ var remote_default = TYPERT_REMOTE;
4341
4385
 
4342
4386
  // src/client/Workbench.tsx
4343
4387
  var import_react6 = require("react");
4344
- var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
4388
+ var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
4345
4389
 
4346
4390
  // src/client/styles.ts
4347
4391
  var text = "var(--dsw-alias-label-primary)";
@@ -4420,7 +4464,7 @@ var styles = {
4420
4464
  };
4421
4465
 
4422
4466
  // src/client/TaskEngineSection.tsx
4423
- var import_react2 = require("react");
4467
+ var import_react = require("react");
4424
4468
  var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
4425
4469
 
4426
4470
  // src/engine.ts
@@ -4513,214 +4557,12 @@ function validateWorkflow(config) {
4513
4557
  return problems;
4514
4558
  }
4515
4559
 
4516
- // src/client/FlowDiagram.tsx
4517
- var import_react = require("react");
4518
- var nodeW = 108;
4519
- var nodeH = 40;
4520
- var stepX = nodeW + 56;
4521
- var margin = 40;
4522
- var nodeTop = 92;
4523
- var nodeCenterY = nodeTop + nodeH / 2;
4524
- var textTertiary2 = "var(--dsw-alias-label-tertiary)";
4525
- var textSecondary2 = "var(--dsw-alias-label-secondary)";
4526
- var border2 = "var(--dsw-alias-border-l2)";
4527
- var surface2 = "var(--dsw-alias-bg-layer-2)";
4528
- var brand = "var(--dsw-alias-state-business-primary)";
4529
- var danger2 = "var(--dsw-alias-state-error-primary)";
4530
- function edgeList(config) {
4531
- return config.transitions.map((t) => {
4532
- const label = (t.requires ?? []).length > 0 ? (t.requires ?? []).join(" \xB7 ") : null;
4533
- const orphaned = !config.stages.includes(t.from) || !config.stages.includes(t.to);
4534
- return { from: t.from, to: t.to, label, orphaned };
4535
- });
4536
- }
4537
- function nodeX(index, stageCount) {
4538
- return index < 0 ? margin + stageCount * stepX + nodeW : margin + index * stepX;
4539
- }
4540
- function FlowDiagram({ config, onAddTransition, onRemoveTransition }) {
4541
- const [dragFrom, setDragFrom] = (0, import_react.useState)(null);
4542
- const [dragPos, setDragPos] = (0, import_react.useState)(null);
4543
- const svgRef = (0, import_react.useRef)(null);
4544
- const stages = config.stages;
4545
- const edges = edgeList(config);
4546
- const stageCount = stages.length;
4547
- const indexOf = (s) => {
4548
- const i = stages.indexOf(s);
4549
- return i < 0 ? -1 : i;
4550
- };
4551
- const width = Math.max(margin * 2 + stageCount * stepX, 360);
4552
- const height = 210;
4553
- const toSvg = (e) => {
4554
- const rect = svgRef.current.getBoundingClientRect();
4555
- return { x: e.clientX - rect.left, y: e.clientY - rect.top };
4556
- };
4557
- const startDrag = (stage, e) => {
4558
- setDragFrom(stage);
4559
- setDragPos(toSvg(e));
4560
- };
4561
- const dropOn = (stage) => {
4562
- if (dragFrom !== null && dragFrom !== stage) onAddTransition(dragFrom, stage);
4563
- setDragFrom(null);
4564
- setDragPos(null);
4565
- };
4566
- const cancelDrag = () => {
4567
- setDragFrom(null);
4568
- setDragPos(null);
4569
- };
4570
- const forwardPath = (from, to) => {
4571
- const x1 = nodeX(from, stageCount) + nodeW;
4572
- const x2 = nodeX(to, stageCount);
4573
- if (to === from + 1) return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4574
- const cx = (x1 + x2) / 2;
4575
- const cy = nodeCenterY + (to - from) * 20;
4576
- return `M ${x1} ${nodeCenterY} Q ${cx} ${cy} ${x2} ${nodeCenterY}`;
4577
- };
4578
- const returnPath = (from, to) => {
4579
- const x1 = nodeX(from, stageCount) + nodeW / 2;
4580
- const x2 = nodeX(to, stageCount) + nodeW / 2;
4581
- const y1 = nodeTop;
4582
- const lift = 26 + Math.abs(from - to) * 6;
4583
- if (from === to) {
4584
- const cx2 = x1 + nodeW / 2 + 8;
4585
- return `M ${x1 + nodeW / 2} ${y1} Q ${cx2} ${y1 - lift} ${x1 + nodeW / 2 + 18} ${y1}`;
4586
- }
4587
- const cx = (x1 + x2) / 2;
4588
- return `M ${x1} ${y1} Q ${cx} ${y1 - lift} ${x2} ${y1}`;
4589
- };
4590
- const forwardLabelY = (from, to) => to === from + 1 ? nodeCenterY - 8 : nodeCenterY + (to - from) * 20 + 16;
4591
- const returnLabelY = (from, to) => nodeTop - 30 - Math.abs(from - to) * 6;
4592
- const dragLine = dragFrom !== null && dragPos !== null ? (() => {
4593
- const from = indexOf(dragFrom);
4594
- const cx = from < 0 ? nodeX(-1, stageCount) + nodeW / 2 : nodeX(from, stageCount) + nodeW / 2;
4595
- const cy = nodeTop;
4596
- return (0, import_react.createElement)("line", {
4597
- x1: cx,
4598
- y1: cy,
4599
- x2: dragPos.x,
4600
- y2: dragPos.y,
4601
- stroke: brand,
4602
- strokeWidth: 1.5,
4603
- strokeDasharray: "5 3",
4604
- pointerEvents: "none"
4605
- });
4606
- })() : null;
4607
- return (0, import_react.createElement)(
4608
- "svg",
4609
- {
4610
- ref: svgRef,
4611
- width,
4612
- height,
4613
- viewBox: `0 0 ${width} ${height}`,
4614
- style: { display: "block", minWidth: "100%", fontFamily: "inherit" },
4615
- role: "img",
4616
- "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE\uFF08\u53EF\u62D6\u62FD\u8FDE\u7EBF\u3001\u70B9\u51FB\u7BAD\u5934\u5220\u9664\uFF09",
4617
- onMouseMove: dragFrom === null ? void 0 : (e) => {
4618
- setDragPos(toSvg(e));
4619
- },
4620
- onMouseUp: cancelDrag,
4621
- onMouseLeave: cancelDrag
4622
- },
4623
- (0, import_react.createElement)(
4624
- "defs",
4625
- null,
4626
- (0, import_react.createElement)("marker", {
4627
- id: "flow-arrow",
4628
- viewBox: "0 0 10 10",
4629
- refX: 8,
4630
- refY: 5,
4631
- markerWidth: 7,
4632
- markerHeight: 7,
4633
- orient: "auto"
4634
- }, (0, import_react.createElement)("path", { d: "M 0 0 L 10 5 L 0 10 z", fill: "context-stroke" }))
4635
- ),
4636
- ...edges.map((edge, i) => {
4637
- const from = indexOf(edge.from);
4638
- const to = indexOf(edge.to);
4639
- const stroke = edge.orphaned ? danger2 : textTertiary2;
4640
- const d = from < to ? forwardPath(from, to) : returnPath(from, to < 0 ? from : to);
4641
- const labelY = from < to ? forwardLabelY(from, to) : returnLabelY(from, to < 0 ? from : to);
4642
- const labelX = from < to ? (nodeX(from, stageCount) + nodeW + nodeX(to, stageCount)) / 2 : (nodeX(from, stageCount) + nodeX(to < 0 ? from : to, stageCount) + nodeW) / 2;
4643
- const common = {
4644
- key: `edge-${i}`,
4645
- d,
4646
- fill: "none",
4647
- stroke,
4648
- strokeWidth: 1.5,
4649
- markerEnd: "url(#flow-arrow)",
4650
- style: { cursor: "pointer" },
4651
- onClick: () => {
4652
- onRemoveTransition(i);
4653
- },
4654
- ...edge.orphaned ? { strokeDasharray: "4 3" } : {}
4655
- };
4656
- const label = edge.label === null ? null : (0, import_react.createElement)("text", {
4657
- x: labelX,
4658
- y: labelY,
4659
- textAnchor: "middle",
4660
- fontSize: 11,
4661
- fill: edge.orphaned ? danger2 : textSecondary2
4662
- }, edge.label);
4663
- return (0, import_react.createElement)(
4664
- "g",
4665
- { key: `edgeg-${i}` },
4666
- (0, import_react.createElement)(
4667
- "path",
4668
- {
4669
- ...common,
4670
- strokeWidth: 6,
4671
- stroke: "transparent",
4672
- fill: "none",
4673
- markerEnd: void 0,
4674
- strokeDasharray: void 0
4675
- }
4676
- ),
4677
- (0, import_react.createElement)("path", common),
4678
- label
4679
- );
4680
- }),
4681
- ...stages.map((stage, i) => {
4682
- const x = nodeX(i, stageCount);
4683
- const isStart = stage === config.start_stage;
4684
- return (0, import_react.createElement)(
4685
- "g",
4686
- {
4687
- key: stage,
4688
- style: { cursor: dragFrom === null ? "crosshair" : "pointer" },
4689
- onMouseDown: (e) => {
4690
- startDrag(stage, e);
4691
- },
4692
- onMouseUp: () => {
4693
- dropOn(stage);
4694
- }
4695
- },
4696
- (0, import_react.createElement)("rect", {
4697
- x,
4698
- y: nodeTop,
4699
- width: nodeW,
4700
- height: nodeH,
4701
- rx: 20,
4702
- ry: 20,
4703
- fill: surface2,
4704
- stroke: isStart ? brand : border2,
4705
- strokeWidth: isStart ? 2 : 1.5
4706
- }),
4707
- (0, import_react.createElement)("text", {
4708
- x: x + nodeW / 2,
4709
- y: nodeCenterY,
4710
- textAnchor: "middle",
4711
- alignmentBaseline: "middle",
4712
- fontSize: 12,
4713
- fill: "var(--dsw-alias-label-primary)",
4714
- fontWeight: isStart ? 600 : 400
4715
- }, stage),
4716
- (0, import_react.createElement)("title", null, `${stage}${stage === config.start_stage ? "\uFF08\u8D77\u59CB\uFF09" : ""} \u2014 \u62D6\u5230\u53E6\u4E00\u9636\u6BB5\u65B0\u5EFA\u6D41\u8F6C`)
4717
- );
4718
- }),
4719
- dragLine
4720
- );
4721
- }
4722
-
4723
- // src/client/templates.ts
4560
+ // src/workflows.ts
4561
+ var FLOW_OPTIONS = [
4562
+ { id: "standard", label: "\u6807\u51C6\u7814\u53D1", description: "\u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838\uFF0C\u542B\u4EA7\u7269\u95E8 + \u786E\u8BA4\u95E8" },
4563
+ { id: "agile", label: "\u654F\u6377\u8F7B\u91CF", description: "\u9700\u6C42 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u5BA1\u67E5\uFF0C\u56DB\u9636\u6BB5\u3001\u5C11\u4EA7\u7269" },
4564
+ { id: "minimal", label: "\u7EAF\u4EE3\u7801", description: "\u5F00\u53D1 \u2192 \u4EA4\u4ED8\uFF0C\u4E24\u9636\u6BB5\uFF0C\u53EA\u7559\u63D0\u4EA4\u95E8\u7981" }
4565
+ ];
4724
4566
  var STANDARD = {
4725
4567
  stages: ["\u9700\u6C42\u8BC4\u5BA1", "\u8BBE\u8BA1", "\u5F00\u53D1", "\u4EA4\u4ED8", "\u4EE3\u7801\u5BA1\u6838", "\u5B8C\u6210"],
4726
4568
  start_stage: "\u9700\u6C42\u8BC4\u5BA1",
@@ -4798,11 +4640,18 @@ var MINIMAL = {
4798
4640
  "\u4EA4\u4ED8": { skills: ["code-commit"], rules: ["commit-conventions"] }
4799
4641
  }
4800
4642
  };
4801
- var WORKFLOW_TEMPLATES = [
4802
- { id: "standard", label: "\u6807\u51C6\u7814\u53D1", description: "\u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u4EE3\u7801\u5BA1\u6838\uFF0C\u542B\u4EA7\u7269\u95E8 + \u786E\u8BA4\u95E8", config: STANDARD },
4803
- { id: "agile", label: "\u654F\u6377\u8F7B\u91CF", description: "\u9700\u6C42 \u2192 \u5F00\u53D1 \u2192 \u4EA4\u4ED8 \u2192 \u5BA1\u67E5\uFF0C\u56DB\u9636\u6BB5\u3001\u5C11\u4EA7\u7269", config: AGILE },
4804
- { id: "minimal", label: "\u7EAF\u4EE3\u7801", description: "\u5F00\u53D1 \u2192 \u4EA4\u4ED8\uFF0C\u4E24\u9636\u6BB5\uFF0C\u53EA\u7559\u63D0\u4EA4\u95E8\u7981", config: MINIMAL }
4805
- ];
4643
+ var FLOW_CONFIGS = {
4644
+ standard: STANDARD,
4645
+ agile: AGILE,
4646
+ minimal: MINIMAL
4647
+ };
4648
+ function resolveFlowConfig(flow, override) {
4649
+ const base = FLOW_CONFIGS[flow] ?? STANDARD;
4650
+ if (override?.stage_bindings !== void 0) {
4651
+ return { ...base, stage_bindings: { ...override.stage_bindings } };
4652
+ }
4653
+ return base;
4654
+ }
4806
4655
 
4807
4656
  // src/client/shared.ts
4808
4657
  function sourceLabel(source) {
@@ -4831,17 +4680,6 @@ function describeError(error) {
4831
4680
 
4832
4681
  // src/client/TaskEngineSection.tsx
4833
4682
  var import_jsx_runtime = require("react/jsx-runtime");
4834
- var text2 = "var(--dsw-alias-label-primary)";
4835
- var textTertiary3 = "var(--dsw-alias-label-tertiary)";
4836
- var control = {
4837
- background: "var(--dsw-alias-bg-layer-1)",
4838
- border: `1px solid ${border}`,
4839
- borderRadius: 8,
4840
- padding: "5px 8px",
4841
- color: text2,
4842
- fontSize: 13,
4843
- minWidth: 0
4844
- };
4845
4683
  var cardV = {
4846
4684
  display: "flex",
4847
4685
  flexDirection: "column",
@@ -4850,38 +4688,25 @@ var cardV = {
4850
4688
  borderRadius: 8,
4851
4689
  padding: "10px 12px"
4852
4690
  };
4853
- var GUARD_OPTIONS = [
4854
- { value: "requirement_confirmation", label: "\u9700\u6C42\u786E\u8BA4", hint: "\u9700\u6C42\u5DF2\u7ECF\u4EBA\u786E\u8BA4\u8FC7" },
4855
- { value: "solution_confirmation", label: "\u65B9\u6848\u786E\u8BA4", hint: "\u65B9\u6848\u5DF2\u7ECF\u4EBA\u786E\u8BA4\u8FC7" },
4856
- { value: "todos_done", label: "\u5F85\u529E\u5B8C\u6210", hint: "\u4EFB\u52A1\u6E05\u5355\u91CC\u7684\u6BCF\u4E00\u9879\u90FD\u5DF2\u52FE\u9009\u5B8C\u6210" },
4857
- { value: "verified", label: "\u5DF2\u9A8C\u8BC1", hint: "\u5DF2\u901A\u8FC7\u9A8C\u8BC1\uFF08\u9AD8\u98CE\u9669\u4EFB\u52A1\u8FD8\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\uFF09" },
4858
- { value: "review_passed", label: "\u8BC4\u5BA1\u901A\u8FC7", hint: "\u4EE3\u7801\u5BA1\u6838\u7ED3\u8BBA\u4E3A\u901A\u8FC7" },
4859
- { value: "artifacts_present", label: "\u4EA7\u7269\u5B57\u6BB5\u5DF2\u586B\u5168", hint: "\u8BE5\u9636\u6BB5\u58F0\u660E\u7684\u6BCF\u4E00\u4E2A\u4EA7\u7269\u5B57\u6BB5\u90FD\u5DF2\u586B\u4E0A\u5185\u5BB9" }
4860
- ];
4861
- var POLICY_OPTIONS = [
4862
- { value: "task", label: "task", hint: "\u68C0\u67E5\u70B9\u4E00\u6B21\u63D0\u4EA4" },
4863
- { value: "item", label: "item", hint: "\u9010\u9879\u63D0\u4EA4 + \u6536\u5C3E TASK" },
4864
- { value: "manual", label: "manual", hint: "\u4EBA\u5DE5\u6307\u793A" }
4865
- ];
4866
4691
  function TaskEngineSection(props) {
4867
4692
  const { remote, workspace } = props;
4868
- const [config, setConfig] = (0, import_react2.useState)(null);
4869
- const [source, setSource] = (0, import_react2.useState)("default");
4870
- const [savedAt, setSavedAt] = (0, import_react2.useState)("");
4871
- const [loadError, setLoadError] = (0, import_react2.useState)("");
4872
- const [loadedProblems, setLoadedProblems] = (0, import_react2.useState)([]);
4873
- const [skills, setSkills] = (0, import_react2.useState)([]);
4874
- const [rules, setRules] = (0, import_react2.useState)([]);
4875
- (0, import_react2.useEffect)(() => {
4693
+ const [flow, setFlow] = (0, import_react.useState)("standard");
4694
+ const [stageBindings, setStageBindings] = (0, import_react.useState)({});
4695
+ const [source, setSource] = (0, import_react.useState)("default");
4696
+ const [savedAt, setSavedAt] = (0, import_react.useState)("");
4697
+ const [loadError, setLoadError] = (0, import_react.useState)("");
4698
+ const [skills, setSkills] = (0, import_react.useState)([]);
4699
+ const [rules, setRules] = (0, import_react.useState)([]);
4700
+ (0, import_react.useEffect)(() => {
4876
4701
  if (workspace === "") return;
4877
4702
  let alive = true;
4878
4703
  setLoadError("");
4879
4704
  void remote.read(workspace).then((result) => {
4880
4705
  if (!alive) return;
4881
4706
  if (result.ok) {
4882
- setConfig(result.value.config);
4707
+ setFlow(result.value.flow);
4708
+ setStageBindings(result.value.config.stage_bindings ?? {});
4883
4709
  setSource(result.value.source);
4884
- setLoadedProblems(result.value.source === "invalid" ? result.value.problems : []);
4885
4710
  setSavedAt("");
4886
4711
  } else {
4887
4712
  setLoadError("\u8BFB\u53D6\u914D\u7F6E\u5931\u8D25\uFF1A" + describeError(result.error));
@@ -4893,7 +4718,7 @@ function TaskEngineSection(props) {
4893
4718
  alive = false;
4894
4719
  };
4895
4720
  }, [remote, workspace]);
4896
- const refreshCatalogs = (0, import_react2.useCallback)(() => {
4721
+ const refreshCatalogs = (0, import_react.useCallback)(() => {
4897
4722
  if (workspace === "") return;
4898
4723
  void remote.listSkills(workspace).then((result) => {
4899
4724
  if (result.ok) setSkills(result.value.skills);
@@ -4904,21 +4729,17 @@ function TaskEngineSection(props) {
4904
4729
  }, () => {
4905
4730
  });
4906
4731
  }, [remote, workspace]);
4907
- (0, import_react2.useEffect)(() => {
4732
+ (0, import_react.useEffect)(() => {
4908
4733
  refreshCatalogs();
4909
4734
  }, [refreshCatalogs]);
4910
- const problems = (0, import_react2.useMemo)(() => config === null ? [] : validateWorkflow(config), [config]);
4911
- const notice = problems.length === 0 && loadedProblems.length > 0 ? loadedProblems : [];
4912
- const addTransition = (from, to) => {
4913
- if (config === null || from === "" || to === "") return;
4914
- update(config, setConfig, (c) => ({
4915
- ...c,
4916
- transitions: [...c.transitions, { from, to }]
4917
- }));
4918
- };
4919
- const removeTransition = (index) => {
4920
- if (config === null) return;
4921
- update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== index) }));
4735
+ const stages = (0, import_react.useMemo)(() => (FLOW_CONFIGS[flow] ?? FLOW_CONFIGS.standard).stages, [flow]);
4736
+ const problems = (0, import_react.useMemo)(
4737
+ () => validateWorkflow(resolveFlowConfig(flow, { stage_bindings: stageBindings })),
4738
+ [flow, stageBindings]
4739
+ );
4740
+ const selectFlow = (id) => {
4741
+ setFlow(id);
4742
+ setStageBindings({ ...FLOW_CONFIGS[id]?.stage_bindings ?? {} });
4922
4743
  };
4923
4744
  if (loadError !== "") {
4924
4745
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
@@ -4932,28 +4753,24 @@ function TaskEngineSection(props) {
4932
4753
  ] })
4933
4754
  ] });
4934
4755
  }
4935
- if (config === null) {
4936
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4937
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4938
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u52A0\u8F7D\u4E2D\u2026" })
4939
- ] });
4940
- }
4941
4756
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4942
4757
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.head, children: [
4943
4758
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
4944
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u8FD9\u4EFD\u914D\u7F6E\u540C\u65F6\u9A71\u52A8 dev_task \u7684\u786C\u95E8\u7981\uFF1B\u6709\u95EE\u9898\u7684\u914D\u7F6E\u4E0D\u4F1A\u88AB\u4FDD\u5B58\u3002" })
4759
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u9009\u4E00\u5957\u6D41\u7A0B\uFF0C\u518D\u7ED9\u6BCF\u4E2A\u8282\u70B9\u6302\u4E0A skill / rule\u3002\u9636\u6BB5\u6D41\u8F6C\u3001\u5B88\u536B\u3001\u63D0\u4EA4\u89C4\u5219\u90FD\u7531\u6D41\u7A0B\u9884\u8BBE\u56FA\u5316\uFF0C\u65E0\u9700\u624B\u5DE5\u914D\u7F6E\u3002" })
4945
4760
  ] }),
4946
4761
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.sourceRow, children: [
4947
4762
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: source === "project" ? "done" : source === "invalid" ? "warning" : "ongoing" }),
4948
4763
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.sourceText, children: sourceText(source) })
4949
4764
  ] }),
4950
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TemplatePicker, { config, setConfig }),
4951
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowCard, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlowDiagram, { config, onAddTransition: addTransition, onRemoveTransition: removeTransition }) }),
4952
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconSettingsOutline16, { size: 16 }), title: "\u9636\u6BB5", hint: "\u6D41\u7A0B\u7531\u8FD9\u4E9B\u9636\u6BB5\u6309\u987A\u5E8F\u7EC4\u6210\u3002\u70B9\u4E00\u4E2A\u6807\u7B7E\u53EF\u5220\u9664\u5B83\uFF08\u8D77\u59CB\u9636\u6BB5\u9664\u5916\uFF09\uFF1B\u7528\u4E2D\u6587\u540D\u66F4\u6613\u61C2\uFF0C\u4F8B\u5982\u300C\u9700\u6C42\u8BC4\u5BA1\u300D\u300C\u5F00\u53D1\u300D\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StageEditor, { config, setConfig }) }),
4953
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u6D41\u8F6C\uFF08\u9636\u6BB5 \u2192 \u9636\u6BB5\uFF0C\u53EF\u5E26\u5B88\u536B\uFF09", defaultOpen: false, hint: "\u6BCF\u6761\u6D41\u8F6C = \u4ECE\u300C\u4E0A\u4E00\u6B65\u300D\u5230\u300C\u4E0B\u4E00\u6B65\u300D\u5141\u8BB8\u8D70\u7684\u8DEF\uFF1B\u53EF\u9644\u52A0\u5B88\u536B\uFF0C\u6EE1\u8DB3\u5B88\u536B\u624D\u653E\u884C\u3002\u4E5F\u53EF\u5728\u6D41\u7A0B\u56FE\u91CC\u76F4\u63A5\u8FDE\u7EBF/\u70B9\u7BAD\u5934\u5220\u9664\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransitionEditor, { config, setConfig }) }),
4954
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCheckOutline16, { size: 16 }), title: "\u4EA7\u7269\uFF08\u6BCF\u4E2A\u9636\u6BB5\u8981\u5199\u6E05\u695A\u7684\u8BB0\u5F55\uFF09", defaultOpen: false, hint: "\u4EA7\u7269 = \u67D0\u9636\u6BB5\u5FC5\u987B\u4EA4\u4ED8\u7684\u4E00\u4EFD\u300C\u586B\u5199\u8BB0\u5F55\u300D\uFF1B\u5B57\u6BB5 = \u8FD9\u4EFD\u8BB0\u5F55\u91CC\u5927\u6A21\u578B\u5FC5\u987B\u586B\u4E0A\u7684\u7A7A\u3002\u4F8B\u5982\u300C\u9700\u6C42\u8BC4\u5BA1\u300D\u9636\u6BB5\u7684\u4EA7\u7269\u300C\u9700\u6C42\u8BF4\u660E\u300D\u8981\u586B scope\uFF08\u8303\u56F4\uFF09\u3001acceptance_criteria\uFF08\u9A8C\u6536\u6807\u51C6\uFF09\u4E24\u4E2A\u5B57\u6BB5\uFF1B\u518D\u7ED9\u300C\u9700\u6C42\u8BC4\u5BA1 \u2192 \u8BBE\u8BA1\u300D\u8FD9\u6761\u6D41\u8F6C\u6302\u4E0A\u300C\u4EA7\u7269\u5B57\u6BB5\u5DF2\u586B\u5168\u300D\u5B88\u536B\uFF0C\u5B57\u6BB5\u6CA1\u586B\u5168\u5C31\u8FDB\u4E0D\u4E86\u4E0B\u4E00\u9636\u6BB5\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactEditor, { config, setConfig }) }),
4955
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconEditOutline16, { size: 16 }), title: "\u63D0\u4EA4\u89C4\u5219", defaultOpen: false, hint: "\u63D0\u4EA4\u7B56\u7565\u3001\u6D88\u606F\u683C\u5F0F\u3001\u63D0\u4EA4\u68C0\u67E5\u70B9\u3001\u6587\u4EF6\u8303\u56F4\uFF0C\u4EE5\u53CA\u9AD8\u98CE\u9669\u662F\u5426\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitEditor, { config, setConfig }) }),
4956
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u9636\u6BB5\u6280\u80FD / \u89C4\u5219", hint: "\u9009\u4E00\u4E2A\u9636\u6BB5\uFF0C\u52FE\u9009\u5B83\u300C\u7528\u4EC0\u4E48 skill\uFF08\u6280\u80FD\uFF09\u300D\u548C\u300C\u5B88\u54EA\u6761 rule\uFF08\u89C4\u5219\uFF09\u300D\u3002\u5927\u6A21\u578B\u8D70\u5230\u8BE5\u9636\u6BB5\u65F6\uFF0C\u53EA\u4F1A\u770B\u5230\u7ED9\u5B83\u6302\u8F7D\u7684\u8FD9\u51E0\u6761\u6280\u80FD\u4E0E\u89C4\u5219\uFF1B\u8981\u65B0\u5EFA\u6216\u6539\u6B63\u6587\uFF0C\u5207\u5230\u4E0A\u65B9\u7684\u300C\u6280\u80FD\u300D/\u300C\u89C4\u5219\u300D\u6807\u7B7E\u9875\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BindingEditor, { config, setConfig, skills, rules }) }),
4765
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4766
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6D41\u7A0B\u9884\u8BBE" }),
4767
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: FLOW_OPTIONS.map((option) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: flow === option.id, onClick: () => {
4768
+ selectFlow(option.id);
4769
+ }, title: option.description, children: option.label }, option.id)) }),
4770
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: FLOW_OPTIONS.find((option) => option.id === flow)?.description })
4771
+ ] }),
4772
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u6D41\u7A0B\u8282\u70B9", hint: "\u8BE5\u6D41\u7A0B\u9884\u8BBE\u56FA\u5B9A\u7684\u9636\u6BB5\u987A\u5E8F\uFF1B\u6539\u53D8\u6D41\u7A0B\u8BF7\u5207\u6362\u4E0A\u9762\u7684\u9884\u8BBE\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: stages.map((stage) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: stage === (FLOW_CONFIGS[flow]?.start_stage ?? ""), title: stage === FLOW_CONFIGS[flow]?.start_stage ? "\u8D77\u59CB\u9636\u6BB5" : void 0, children: stage }, stage)) }) }),
4773
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconSettingsOutline16, { size: 16 }), title: "\u9636\u6BB5\u6280\u80FD / \u89C4\u5219", hint: "\u9009\u4E00\u4E2A\u8282\u70B9\uFF0C\u52FE\u9009\u5B83\u300C\u7528\u4EC0\u4E48 skill\uFF08\u6280\u80FD\uFF09\u300D\u548C\u300C\u5B88\u54EA\u6761 rule\uFF08\u89C4\u5219\uFF09\u300D\u3002\u6A21\u578B\u8D70\u5230\u8BE5\u8282\u70B9\u65F6\u53EA\u4F1A\u770B\u5230\u4F60\u6302\u8F7D\u7684\u8FD9\u51E0\u6761\uFF1B\u8981\u65B0\u5EFA\u6216\u6539\u6B63\u6587\uFF0C\u5207\u5230\u4E0A\u65B9\u7684\u300C\u6280\u80FD\u300D/\u300C\u89C4\u5219\u300D\u6807\u7B7E\u9875\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BindingEditor, { stages, stageBindings, setStageBindings, skills, rules }) }),
4957
4774
  problems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.problems, children: [
4958
4775
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.problemsTitle, children: [
4959
4776
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "error" }),
@@ -4962,16 +4779,9 @@ function TaskEngineSection(props) {
4962
4779
  " \u4E2A\u95EE\u9898\uFF08\u4FDD\u5B58\u4F1A\u88AB\u62D2\u7EDD\uFF09"
4963
4780
  ] }),
4964
4781
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.problemsList, children: problems.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: p }, p)) })
4965
- ] }) : notice.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.noticeBox, children: [
4966
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.noticeTitle, children: [
4967
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "warning" }),
4968
- "\u539F .dsh/eng.json \u65E0\u6CD5\u89E3\u6790"
4969
- ] }),
4970
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("ul", { style: styles.problemsList, children: notice.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("li", { children: p }, p)) }),
4971
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u5F53\u524D\u663E\u793A\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF1B\u70B9\u51FB\u4FDD\u5B58\u4F1A\u8986\u76D6\u539F\u6587\u4EF6\u3002" })
4972
4782
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.okText, children: [
4973
4783
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "done" }),
4974
- source === "default" ? "\u5F53\u524D\u4F7F\u7528\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E" : "\u914D\u7F6E\u6709\u6548"
4784
+ source === "project" ? "\u914D\u7F6E\u6709\u6548" : "\u4F7F\u7528\u6D41\u7A0B\u9884\u8BBE\u9ED8\u8BA4\u7ED1\u5B9A\uFF08\u5C1A\u672A\u4FDD\u5B58\uFF09"
4975
4785
  ] }),
4976
4786
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.actions, children: [
4977
4787
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -4982,7 +4792,7 @@ function TaskEngineSection(props) {
4982
4792
  icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCheckOutline16, { size: 16 }),
4983
4793
  disabled: problems.length > 0,
4984
4794
  onClick: () => {
4985
- void save(remote, config, workspace, setSavedAt, setSource, setLoadedProblems);
4795
+ void save(remote, flow, stageBindings, workspace, setSavedAt, setSource);
4986
4796
  },
4987
4797
  children: "\u4FDD\u5B58\u5230 .dsh/eng.json"
4988
4798
  }
@@ -4999,13 +4809,13 @@ function sourceText(source) {
4999
4809
  case "project":
5000
4810
  return "\u9879\u76EE\u914D\u7F6E\uFF08.dsh/eng.json\uFF09";
5001
4811
  case "invalid":
5002
- return "\u539F\u6587\u4EF6\u65E0\u6CD5\u89E3\u6790\uFF0C\u663E\u793A\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E";
4812
+ return "\u539F\u6587\u4EF6\u65E0\u6CD5\u89E3\u6790\uFF0C\u663E\u793A\u6807\u51C6\u9884\u8BBE";
5003
4813
  default:
5004
- return "\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF08\u5DE5\u4F5C\u533A\u5C1A\u65E0 .dsh/eng.json\uFF09";
4814
+ return "\u6807\u51C6\u9884\u8BBE\uFF08\u5DE5\u4F5C\u533A\u5C1A\u65E0 .dsh/eng.json\uFF09";
5005
4815
  }
5006
4816
  }
5007
- function SectionCard({ icon, title, hint, defaultOpen = true, children }) {
5008
- const [open, setOpen] = (0, import_react2.useState)(defaultOpen);
4817
+ function SectionCard({ icon, title, hint, children }) {
4818
+ const [open, setOpen] = (0, import_react.useState)(true);
5009
4819
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.section, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5010
4820
  import_dsh_client_ui_primitives.DisclosureRow,
5011
4821
  {
@@ -5024,301 +4834,36 @@ function SectionCard({ icon, title, hint, defaultOpen = true, children }) {
5024
4834
  }
5025
4835
  ) });
5026
4836
  }
5027
- function TemplatePicker({ config, setConfig }) {
5028
- const apply2 = (id) => {
5029
- const template = WORKFLOW_TEMPLATES.find((t) => t.id === id);
5030
- if (template === void 0) return;
5031
- setConfig(cloneConfig(template.config));
5032
- };
5033
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
5034
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6D41\u7A0B\u6A21\u677F\uFF08\u70B9\u4E00\u4E0B\u6574\u4F53\u5957\u7528\uFF0C\u4F1A\u8986\u76D6\u5F53\u524D\u7F16\u8F91\uFF09" }),
5035
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: WORKFLOW_TEMPLATES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: config.stages.join(",") === t.config.stages.join(","), onClick: () => {
5036
- apply2(t.id);
5037
- }, title: t.description, children: t.label }, t.id)) }),
5038
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: "\u4E0D\u786E\u5B9A\u600E\u4E48\u914D\u5C31\u4ECE\u300C\u6807\u51C6\u7814\u53D1\u300D\u8D77\u6B65\uFF0C\u5957\u7528\u540E\u518D\u5FAE\u8C03\uFF1B\u4E5F\u53EF\u4EE5\u5148\u5957\u6A21\u677F\uFF0C\u518D\u7528\u4E0B\u9762\u7684\u5361\u7247\u6539\u3002" })
5039
- ] });
5040
- }
5041
- function cloneConfig(config) {
5042
- return JSON.parse(JSON.stringify(config));
5043
- }
5044
- function StageEditor({ config, setConfig }) {
5045
- const [draft, setDraft] = (0, import_react2.useState)("");
5046
- const addStage = () => {
5047
- const name = draft.trim();
5048
- if (name === "" || config.stages.includes(name)) return;
5049
- update(config, setConfig, (c) => ({ ...c, stages: [...c.stages, name] }));
5050
- setDraft("");
5051
- };
5052
- const removeStage = (stage) => {
5053
- update(config, setConfig, (c) => {
5054
- const stages = c.stages.filter((s) => s !== stage);
5055
- return {
5056
- ...c,
5057
- stages,
5058
- start_stage: c.start_stage === stage ? stages[0] ?? "" : c.start_stage
5059
- };
5060
- });
5061
- };
5062
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5063
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: config.stages.map((stage) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5064
- import_dsh_client_ui_primitives.Pill,
5065
- {
5066
- active: stage === config.start_stage,
5067
- onClick: stage === config.start_stage ? void 0 : () => {
5068
- removeStage(stage);
5069
- },
5070
- title: stage === config.start_stage ? "\u8D77\u59CB\u9636\u6BB5" : `\u70B9\u51FB\u5220\u9664 ${stage}`,
5071
- children: stage
5072
- },
5073
- stage
5074
- )) }),
5075
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5076
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5077
- "input",
5078
- {
5079
- style: { ...control, flex: "1 1 180px" },
5080
- placeholder: "\u65B0\u9636\u6BB5\u540D",
5081
- value: draft,
5082
- onChange: (ev) => {
5083
- setDraft(ev.target.value);
5084
- },
5085
- onKeyDown: (ev) => {
5086
- if (ev.key === "Enter") addStage();
5087
- }
5088
- }
5089
- ),
5090
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }), disabled: draft.trim() === "", onClick: addStage, children: "\u6DFB\u52A0\u9636\u6BB5" })
5091
- ] }),
5092
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5093
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u8D77\u59CB\u9636\u6BB5" }),
5094
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5095
- "select",
5096
- {
5097
- style: control,
5098
- value: config.start_stage,
5099
- onChange: (ev) => {
5100
- update(config, setConfig, (c) => ({ ...c, start_stage: ev.target.value }));
5101
- },
5102
- children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s))
5103
- }
5104
- )
5105
- ] })
5106
- ] });
5107
- }
5108
- function TransitionEditor({ config, setConfig }) {
5109
- const setTransition = (i, patch) => {
5110
- update(config, setConfig, (c) => ({
5111
- ...c,
5112
- transitions: c.transitions.map((t, j) => j === i ? { ...t, ...patch } : t)
5113
- }));
5114
- };
5115
- const toggleGuard = (i, guard) => {
5116
- const t = config.transitions[i];
5117
- if (t === void 0) return;
5118
- const current = t.requires ?? [];
5119
- const next = current.includes(guard) ? current.filter((g) => g !== guard) : [...current, guard];
5120
- setTransition(i, { requires: next });
5121
- };
5122
- const remove = (i) => {
5123
- update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== i) }));
5124
- };
5125
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5126
- config.transitions.map((t, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5127
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5128
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: t.from, onChange: (ev) => {
5129
- setTransition(i, { from: ev.target.value });
5130
- }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
5131
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: { color: textTertiary3 }, children: "\u2192" }),
5132
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: t.to, onChange: (ev) => {
5133
- setTransition(i, { to: ev.target.value });
5134
- }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
5135
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 }), onClick: () => {
5136
- remove(i);
5137
- }, title: "\u5220\u9664\u8FD9\u6761\u6D41\u8F6C" })
5138
- ] }),
5139
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: GUARD_OPTIONS.map((g) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5140
- import_dsh_client_ui_primitives.Pill,
5141
- {
5142
- active: (t.requires ?? []).includes(g.value),
5143
- onClick: () => {
5144
- toggleGuard(i, g.value);
5145
- },
5146
- title: g.hint,
5147
- children: g.label
5148
- },
5149
- g.value
5150
- )) })
5151
- ] }, i)),
5152
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5153
- import_dsh_client_ui_primitives.Button,
5154
- {
5155
- variant: "outline",
5156
- size: "sm",
5157
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
5158
- onClick: () => {
5159
- update(config, setConfig, (c) => ({ ...c, transitions: [...c.transitions, { from: c.start_stage, to: c.start_stage }] }));
5160
- },
5161
- children: "\u52A0\u4E00\u6761\u6D41\u8F6C"
5162
- }
5163
- ) })
5164
- ] });
5165
- }
5166
- function ArtifactEditor({ config, setConfig }) {
5167
- const setArtifact = (i, patch) => {
5168
- update(config, setConfig, (c) => ({
5169
- ...c,
5170
- artifacts: c.artifacts.map((a, j) => j === i ? { ...a, ...patch } : a)
5171
- }));
5172
- };
5173
- const remove = (i) => {
5174
- update(config, setConfig, (c) => ({ ...c, artifacts: c.artifacts.filter((_, j) => j !== i) }));
5175
- };
5176
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5177
- config.artifacts.map((a, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5178
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5179
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("select", { style: control, value: a.stage, onChange: (ev) => {
5180
- setArtifact(i, { stage: ev.target.value });
5181
- }, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("option", { value: s, children: s }, s)) }),
5182
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 110px" }, placeholder: "\u82F1\u6587 id\uFF08\u5982 requirement\uFF09", value: a.id, onChange: (ev) => {
5183
- setArtifact(i, { id: ev.target.value });
5184
- } }),
5185
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 130px" }, placeholder: "\u540D\u79F0\uFF08\u7ED9\u4EBA\u770B\uFF0C\u5982 \u9700\u6C42\u8BF4\u660E\uFF09", value: a.name, onChange: (ev) => {
5186
- setArtifact(i, { name: ev.target.value });
5187
- } }),
5188
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Button, { variant: "ghost", size: "sm", icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconCloseOutline16, { size: 14 }), onClick: () => {
5189
- remove(i);
5190
- }, title: "\u5220\u9664\u8FD9\u4E2A\u4EA7\u7269" })
5191
- ] }),
5192
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5193
- "input",
5194
- {
5195
- style: control,
5196
- placeholder: "\u8981\u586B\u7684\u5B57\u6BB5\uFF0C\u9017\u53F7\u5206\u9694\uFF08\u5982 scope, acceptance_criteria\uFF09",
5197
- value: a.fields.join(", "),
5198
- onChange: (ev) => {
5199
- setArtifact(i, { fields: splitList(ev.target.value) });
5200
- }
5201
- }
5202
- )
5203
- ] }, i)),
5204
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5205
- import_dsh_client_ui_primitives.Button,
5206
- {
5207
- variant: "outline",
5208
- size: "sm",
5209
- icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 14 }),
5210
- onClick: () => {
5211
- update(config, setConfig, (c) => ({ ...c, artifacts: [...c.artifacts, { stage: c.start_stage, id: "", name: "", fields: [] }] }));
5212
- },
5213
- children: "\u52A0\u4E00\u4E2A\u4EA7\u7269"
5214
- }
5215
- ) })
5216
- ] });
5217
- }
5218
- function CommitEditor({ config, setConfig }) {
5219
- const commit = config.commit;
5220
- const setCommit = (patch) => {
5221
- update(config, setConfig, (c) => ({ ...c, commit: { ...c.commit, ...patch } }));
5222
- };
5223
- const toggleCheckpoint = (stage) => {
5224
- const current = commit.checkpoints;
5225
- const next = current.includes(stage) ? current.filter((s) => s !== stage) : [...current, stage];
5226
- setCommit({ checkpoints: next });
5227
- };
5228
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5229
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
5230
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u7B56\u7565" }),
5231
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: POLICY_OPTIONS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: commit.policy === p.value, onClick: () => {
5232
- setCommit({ policy: p.value });
5233
- }, title: p.hint, children: p.label }, p.value)) }),
5234
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: POLICY_OPTIONS.find((p) => p.value === commit.policy)?.hint })
5235
- ] }),
5236
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5237
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.field, children: [
5238
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u6D88\u606F\u683C\u5F0F\uFF08\u6B63\u5219\uFF0C\u7559\u7A7A\u5219\u4E0D\u6821\u9A8C\uFF09" }),
5239
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, value: commit.message_pattern, onChange: (ev) => {
5240
- setCommit({ message_pattern: ev.target.value });
5241
- } })
5242
- ] }),
5243
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: styles.field, children: [
5244
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u683C\u5F0F\u8BF4\u660E" }),
5245
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, value: commit.message_hint, onChange: (ev) => {
5246
- setCommit({ message_hint: ev.target.value });
5247
- } })
5248
- ] })
5249
- ] }),
5250
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
5251
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u63D0\u4EA4\u68C0\u67E5\u70B9\uFF08\u5230\u8FBE\u8FD9\u4E9B\u9636\u6BB5\u524D\u9700\u63D0\u4EA4\uFF09" }),
5252
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: commit.checkpoints.includes(s), onClick: () => {
5253
- toggleCheckpoint(s);
5254
- }, title: `\u68C0\u67E5\u70B9 ${s}`, children: s }, s)) })
5255
- ] }),
5256
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
5257
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inline, children: [
5258
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5259
- "input",
5260
- {
5261
- type: "checkbox",
5262
- checked: commit.file_scope,
5263
- onChange: (ev) => {
5264
- setCommit({ file_scope: ev.target.checked });
5265
- }
5266
- }
5267
- ),
5268
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u6587\u4EF6\u8303\u56F4\u5F00\u5173\uFF08\u63D0\u4EA4\u53EA\u5141\u8BB8\u4EFB\u52A1\u91CC\u58F0\u660E\u7684\u6587\u4EF6\uFF09" })
5269
- ] }),
5270
- /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.inline, children: [
5271
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5272
- "input",
5273
- {
5274
- type: "checkbox",
5275
- checked: config.high_risk_requires_verification,
5276
- onChange: (ev) => {
5277
- update(config, setConfig, (c) => ({ ...c, high_risk_requires_verification: ev.target.checked }));
5278
- }
5279
- }
5280
- ),
5281
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u9AD8\u98CE\u9669\u4EFB\u52A1\u5FC5\u987B\u9644\u9A8C\u8BC1\u8BC1\u636E\u624D\u80FD\u8FC7 verified \u5B88\u536B" })
5282
- ] })
5283
- ] })
5284
- ] });
5285
- }
5286
- function BindingEditor({ config, setConfig, skills, rules }) {
5287
- const [stage, setStage] = (0, import_react2.useState)(config.stages[0] ?? "");
5288
- const currentStage = config.stages.includes(stage) ? stage : config.stages[0] ?? "";
4837
+ function BindingEditor({ stages, stageBindings, setStageBindings, skills, rules }) {
4838
+ const [stage, setStage] = (0, import_react.useState)(stages[0] ?? "");
4839
+ const currentStage = stages.includes(stage) ? stage : stages[0] ?? "";
5289
4840
  const toggleSkill = (skill) => {
5290
4841
  if (currentStage === "") return;
5291
- update(config, setConfig, (c) => {
5292
- const bindings = c.stage_bindings ?? {};
5293
- const current = bindings[currentStage]?.skills ?? [];
5294
- const next = current.includes(skill) ? current.filter((s) => s !== skill) : [...current, skill].sort();
5295
- return { ...c, stage_bindings: { ...bindings, [currentStage]: { ...bindings[currentStage] ?? {}, skills: next } } };
5296
- });
4842
+ const current = stageBindings[currentStage]?.skills ?? [];
4843
+ const next = current.includes(skill) ? current.filter((s) => s !== skill) : [...current, skill].sort();
4844
+ setStageBindings({ ...stageBindings, [currentStage]: { ...stageBindings[currentStage] ?? {}, skills: next } });
5297
4845
  };
5298
4846
  const toggleRule = (rule) => {
5299
4847
  if (currentStage === "") return;
5300
- update(config, setConfig, (c) => {
5301
- const bindings = c.stage_bindings ?? {};
5302
- const current = bindings[currentStage]?.rules ?? [];
5303
- const next = current.includes(rule) ? current.filter((s) => s !== rule) : [...current, rule].sort();
5304
- return { ...c, stage_bindings: { ...bindings, [currentStage]: { ...bindings[currentStage] ?? {}, rules: next } } };
5305
- });
4848
+ const current = stageBindings[currentStage]?.rules ?? [];
4849
+ const next = current.includes(rule) ? current.filter((s) => s !== rule) : [...current, rule].sort();
4850
+ setStageBindings({ ...stageBindings, [currentStage]: { ...stageBindings[currentStage] ?? {}, rules: next } });
5306
4851
  };
5307
4852
  if (currentStage === "") {
5308
- return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u5148\u5728\u300C\u9636\u6BB5\u300D\u91CC\u52A0\u4E00\u4E2A\u9636\u6BB5\uFF0C\u518D\u56DE\u6765\u6302 skill / rule\u3002" });
4853
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: "\u5F53\u524D\u6D41\u7A0B\u6CA1\u6709\u8282\u70B9\u3002" });
5309
4854
  }
5310
- const boundSkills = config.stage_bindings?.[currentStage]?.skills ?? [];
5311
- const boundRules = config.stage_bindings?.[currentStage]?.rules ?? [];
4855
+ const boundSkills = stageBindings[currentStage]?.skills ?? [];
4856
+ const boundRules = stageBindings[currentStage]?.rules ?? [];
5312
4857
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5313
4858
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
5314
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u9009\u62E9\u9636\u6BB5\uFF08\u6539\u54EA\u4E2A\u9636\u6BB5\u7684\u6302\u8F7D\uFF09" }),
5315
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: config.stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: s === currentStage, onClick: () => {
4859
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u9009\u62E9\u8282\u70B9\uFF08\u6539\u54EA\u4E2A\u8282\u70B9\u7684\u6302\u8F7D\uFF09" }),
4860
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: stages.map((s) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: s === currentStage, onClick: () => {
5316
4861
  setStage(s);
5317
4862
  }, children: s }, s)) })
5318
4863
  ] }),
5319
4864
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5320
4865
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.bindingStage, children: [
5321
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "skill\uFF08\u8FD9\u4E2A\u9636\u6BB5\u505A\u4EC0\u4E48\uFF09" }),
4866
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "skill\uFF08\u8FD9\u4E2A\u8282\u70B9\u505A\u4EC0\u4E48\uFF09" }),
5322
4867
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: skills.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.sourceBadge, children: "\u6682\u65E0\u53EF\u7528 skill \u2014\u2014 \u5207\u5230\u4E0A\u65B9\u300C\u6280\u80FD\u300D\u6807\u7B7E\u9875\u65B0\u5EFA" }) : skills.map((sk) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Pill, { active: boundSkills.includes(sk.name), onClick: () => {
5323
4868
  toggleSkill(sk.name);
5324
4869
  }, title: `${sk.description}\uFF08${sourceLabel(sk.source)}\uFF09`, children: [
@@ -5331,7 +4876,7 @@ function BindingEditor({ config, setConfig, skills, rules }) {
5331
4876
  ] }, sk.name)) })
5332
4877
  ] }),
5333
4878
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.bindingStage, children: [
5334
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "rule\uFF08\u8FD9\u4E2A\u9636\u6BB5\u5B88\u4EC0\u4E48\uFF09" }),
4879
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "rule\uFF08\u8FD9\u4E2A\u8282\u70B9\u5B88\u4EC0\u4E48\uFF09" }),
5335
4880
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.chips, children: rules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.sourceBadge, children: "\u6682\u65E0\u53EF\u7528 rule \u2014\u2014 \u5207\u5230\u4E0A\u65B9\u300C\u89C4\u5219\u300D\u6807\u7B7E\u9875\u65B0\u5EFA" }) : rules.map((rule) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_dsh_client_ui_primitives.Pill, { active: boundRules.includes(rule.name), onClick: () => {
5336
4881
  toggleRule(rule.name);
5337
4882
  }, title: `\u6765\u6E90\uFF1A${sourceLabel(rule.source)}`, children: [
@@ -5343,31 +4888,148 @@ function BindingEditor({ config, setConfig, skills, rules }) {
5343
4888
  ] }) : null
5344
4889
  ] }, rule.name)) })
5345
4890
  ] }),
5346
- boundSkills.length === 0 && boundRules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: "\u8FD9\u4E2A\u9636\u6BB5\u8FD8\u6CA1\u6302\u4EFB\u4F55 skill / rule \u2014\u2014 \u70B9\u4E0A\u9762\u7684\u540D\u79F0\u52FE\u9009\u3002" }) : null
4891
+ boundSkills.length === 0 && boundRules.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.hint, children: "\u8FD9\u4E2A\u8282\u70B9\u8FD8\u6CA1\u6302\u4EFB\u4F55 skill / rule \u2014\u2014 \u70B9\u4E0A\u9762\u7684\u540D\u79F0\u52FE\u9009\u3002" }) : null
5347
4892
  ] })
5348
4893
  ] });
5349
4894
  }
5350
- function update(config, setConfig, fn) {
5351
- setConfig(fn(config));
5352
- }
5353
- async function save(remote, config, workspace, setSavedAt, setSource, setLoadedProblems) {
5354
- const result = await remote.write({ path: workspace, config });
4895
+ async function save(remote, flow, stageBindings, workspace, setSavedAt, setSource) {
4896
+ const result = await remote.write({ path: workspace, flow, stage_bindings: stageBindings });
5355
4897
  if (!result.ok) {
5356
4898
  setSavedAt("\u4FDD\u5B58\u5931\u8D25\uFF1A" + describeError(result.error));
5357
4899
  return;
5358
4900
  }
5359
4901
  setSource(result.value.source);
5360
- setLoadedProblems(result.value.source === "invalid" ? result.value.problems : []);
5361
4902
  setSavedAt(result.value.ok ? "\u5DF2\u4FDD\u5B58" : "");
5362
4903
  }
5363
- function splitList(textInput) {
5364
- return textInput.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
5365
- }
5366
4904
 
5367
4905
  // src/client/SkillManager.tsx
5368
4906
  var import_react3 = require("react");
4907
+ var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
4908
+
4909
+ // src/client/ResourceModal.tsx
4910
+ var import_react2 = require("react");
4911
+ var import_react_dom = require("react-dom");
5369
4912
  var import_dsh_client_ui_primitives2 = require("@deepseek-ai/dsh-client-ui-primitives");
5370
- var control2 = {
4913
+ var rootStyle = {
4914
+ position: "fixed",
4915
+ inset: 0,
4916
+ zIndex: 1e3,
4917
+ display: "flex",
4918
+ alignItems: "center",
4919
+ justifyContent: "center",
4920
+ padding: 24
4921
+ };
4922
+ var maskStyle = {
4923
+ position: "absolute",
4924
+ inset: 0,
4925
+ background: "var(--dsw-alias-bg-mask-1)",
4926
+ backdropFilter: "var(--dsw-mask-blur)"
4927
+ };
4928
+ var dialogStyle = {
4929
+ position: "relative",
4930
+ zIndex: 1,
4931
+ display: "flex",
4932
+ flexDirection: "column",
4933
+ width: "min(760px, 100%)",
4934
+ maxHeight: "85vh",
4935
+ borderRadius: 24,
4936
+ background: "var(--dsw-alias-bg-layer-2)",
4937
+ boxShadow: "var(--dsw-elevation-prominent)",
4938
+ overflow: "hidden"
4939
+ };
4940
+ var headerStyle = {
4941
+ display: "flex",
4942
+ alignItems: "center",
4943
+ justifyContent: "space-between",
4944
+ gap: 8,
4945
+ padding: "18px 18px 10px 24px",
4946
+ flex: "none"
4947
+ };
4948
+ var titleStyle = {
4949
+ margin: 0,
4950
+ fontSize: 16,
4951
+ lineHeight: "24px",
4952
+ fontWeight: 500,
4953
+ color: "var(--dsw-alias-label-primary)"
4954
+ };
4955
+ var closeBtnStyle = {
4956
+ flex: "none",
4957
+ display: "inline-flex",
4958
+ alignItems: "center",
4959
+ justifyContent: "center",
4960
+ width: 28,
4961
+ height: 28,
4962
+ border: "none",
4963
+ borderRadius: 8,
4964
+ background: "transparent",
4965
+ cursor: "pointer",
4966
+ color: "var(--dsw-alias-label-secondary)",
4967
+ padding: 0
4968
+ };
4969
+ var descStyle = {
4970
+ margin: 0,
4971
+ padding: "0 24px",
4972
+ fontSize: 13,
4973
+ lineHeight: "20px",
4974
+ color: "var(--dsw-alias-label-secondary)",
4975
+ flex: "none"
4976
+ };
4977
+ var bodyStyle = {
4978
+ flex: "1 1 auto",
4979
+ minHeight: 0,
4980
+ overflowY: "auto",
4981
+ padding: "16px 24px 4px",
4982
+ display: "flex",
4983
+ flexDirection: "column"
4984
+ };
4985
+ var footerStyle = {
4986
+ display: "flex",
4987
+ alignItems: "center",
4988
+ justifyContent: "flex-end",
4989
+ gap: 8,
4990
+ padding: "12px 24px 20px",
4991
+ flex: "none"
4992
+ };
4993
+ function ResourceModal({ title, description, onClose, footer, children }) {
4994
+ (0, import_react2.useEffect)(() => {
4995
+ const onKey = (e) => {
4996
+ if (e.key === "Escape") onClose();
4997
+ };
4998
+ document.addEventListener("keydown", onKey);
4999
+ return () => {
5000
+ document.removeEventListener("keydown", onKey);
5001
+ };
5002
+ }, [onClose]);
5003
+ return (0, import_react_dom.createPortal)(
5004
+ (0, import_react2.createElement)(
5005
+ "div",
5006
+ { style: rootStyle, role: "presentation" },
5007
+ (0, import_react2.createElement)("div", { style: maskStyle, "aria-hidden": true, onClick: onClose }),
5008
+ (0, import_react2.createElement)(
5009
+ "div",
5010
+ { style: dialogStyle, role: "dialog", "aria-modal": true, "aria-label": title },
5011
+ (0, import_react2.createElement)(
5012
+ "div",
5013
+ { style: headerStyle },
5014
+ (0, import_react2.createElement)("h2", { style: titleStyle }, title),
5015
+ (0, import_react2.createElement)(
5016
+ "button",
5017
+ { type: "button", style: closeBtnStyle, "aria-label": "\u5173\u95ED", onClick: onClose },
5018
+ (0, import_react2.createElement)(import_dsh_client_ui_primitives2.IconCloseOutline16, { size: 14 })
5019
+ )
5020
+ ),
5021
+ description !== void 0 && description !== "" ? (0, import_react2.createElement)("p", { style: descStyle }, description) : null,
5022
+ (0, import_react2.createElement)("div", { style: bodyStyle }, children),
5023
+ footer !== void 0 ? (0, import_react2.createElement)("div", { style: footerStyle }, footer) : null
5024
+ )
5025
+ ),
5026
+ document.body
5027
+ );
5028
+ }
5029
+
5030
+ // src/client/SkillManager.tsx
5031
+ var MD_LABELS = { code: { copyLabel: "\u590D\u5236", copiedLabel: "\u5DF2\u590D\u5236" }, footnotes: "\u811A\u6CE8" };
5032
+ var control = {
5371
5033
  background: "var(--dsw-alias-bg-layer-1)",
5372
5034
  border: "1px solid var(--dsw-alias-border-l2)",
5373
5035
  borderRadius: 8,
@@ -5385,6 +5047,7 @@ var card = {
5385
5047
  padding: "10px 12px"
5386
5048
  };
5387
5049
  function levelOf(source) {
5050
+ if (source === "bundled") return "bundled";
5388
5051
  return source.startsWith("user") ? "user" : "project";
5389
5052
  }
5390
5053
  function SkillManager({ workspace, remote }) {
@@ -5392,6 +5055,8 @@ function SkillManager({ workspace, remote }) {
5392
5055
  const [level, setLevel] = (0, import_react3.useState)("project");
5393
5056
  const [editing, setEditing] = (0, import_react3.useState)(null);
5394
5057
  const [creating, setCreating] = (0, import_react3.useState)(false);
5058
+ const [viewing, setViewing] = (0, import_react3.useState)(false);
5059
+ const [confirming, setConfirming] = (0, import_react3.useState)(null);
5395
5060
  const [name, setName] = (0, import_react3.useState)("");
5396
5061
  const [description, setDescription] = (0, import_react3.useState)("");
5397
5062
  const [whenToUse, setWhenToUse] = (0, import_react3.useState)("");
@@ -5406,9 +5071,20 @@ function SkillManager({ workspace, remote }) {
5406
5071
  (0, import_react3.useEffect)(() => {
5407
5072
  refresh();
5408
5073
  }, [refresh]);
5074
+ const closeForm = () => {
5075
+ setEditing(null);
5076
+ setCreating(false);
5077
+ setViewing(false);
5078
+ setName("");
5079
+ setDescription("");
5080
+ setWhenToUse("");
5081
+ setBody("");
5082
+ setMsg("");
5083
+ };
5409
5084
  const startCreate = () => {
5410
5085
  setEditing(null);
5411
5086
  setCreating(true);
5087
+ setViewing(false);
5412
5088
  setLevel("project");
5413
5089
  setName("");
5414
5090
  setDescription("");
@@ -5416,13 +5092,9 @@ function SkillManager({ workspace, remote }) {
5416
5092
  setBody("");
5417
5093
  setMsg("");
5418
5094
  };
5419
- const startEdit = (sk) => {
5420
- const lvl = levelOf(sk.source);
5421
- setEditing(sk.name);
5422
- setCreating(false);
5423
- setLevel(lvl);
5095
+ const loadSkill = (skillName, lvl) => {
5424
5096
  setMsg("");
5425
- void remote.readSkill({ name: sk.name, level: lvl, path: workspace }).then((r) => {
5097
+ void remote.readSkill({ name: skillName, level: lvl, path: workspace }).then((r) => {
5426
5098
  if (r.ok && r.value.ok) {
5427
5099
  setName(r.value.name);
5428
5100
  setDescription(r.value.description);
@@ -5433,6 +5105,20 @@ function SkillManager({ workspace, remote }) {
5433
5105
  }
5434
5106
  });
5435
5107
  };
5108
+ const startEdit = (sk) => {
5109
+ const lvl = levelOf(sk.source);
5110
+ setEditing(sk.name);
5111
+ setCreating(false);
5112
+ setViewing(false);
5113
+ setLevel(lvl === "user" ? "user" : "project");
5114
+ loadSkill(sk.name, lvl);
5115
+ };
5116
+ const startView = (sk) => {
5117
+ setEditing(sk.name);
5118
+ setCreating(false);
5119
+ setViewing(true);
5120
+ loadSkill(sk.name, "bundled");
5121
+ };
5436
5122
  const submit = async () => {
5437
5123
  setMsg("");
5438
5124
  const target = editing ?? name;
@@ -5457,104 +5143,143 @@ function SkillManager({ workspace, remote }) {
5457
5143
  setCreating(false);
5458
5144
  refresh();
5459
5145
  };
5146
+ const remove = async (sk) => {
5147
+ setMsg("");
5148
+ const lvl = levelOf(sk.source);
5149
+ if (lvl === "bundled") return;
5150
+ const result = await remote.deleteSkill({ name: sk.name, level: lvl, path: workspace });
5151
+ if (!result.ok) {
5152
+ setMsg("\u5220\u9664\u5931\u8D25\uFF1A" + describeError(result.error));
5153
+ setConfirming(null);
5154
+ return;
5155
+ }
5156
+ if (!result.value.ok) {
5157
+ setMsg("\u5220\u9664\u5931\u8D25\uFF1A" + (result.value.error ?? "\u672A\u77E5\u9519\u8BEF"));
5158
+ setConfirming(null);
5159
+ return;
5160
+ }
5161
+ setMsg(`\u5DF2\u5220\u9664 skill\u300C${result.value.name}\u300D`);
5162
+ if (editing === sk.name) setEditing(null);
5163
+ setConfirming(null);
5164
+ refresh();
5165
+ };
5460
5166
  const formOpen = editing !== null || creating;
5461
- return (0, import_react3.createElement)(
5167
+ const renderRow = (sk) => (0, import_react3.createElement)(
5462
5168
  "div",
5463
- { style: styles.section },
5169
+ {
5170
+ key: sk.name,
5171
+ style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--dsw-alias-border-l2)" }
5172
+ },
5464
5173
  (0, import_react3.createElement)(
5465
5174
  "div",
5466
- { style: card },
5175
+ { style: { display: "flex", flexDirection: "column", gap: 2, minWidth: 0 } },
5467
5176
  (0, import_react3.createElement)(
5468
- "div",
5469
- { style: styles.bindingStage },
5470
- (0, import_react3.createElement)("span", { style: styles.bindingLabel }, "\u5DF2\u5B89\u88C5\u7684 skill"),
5471
- skills.length === 0 ? (0, import_react3.createElement)("p", { style: styles.muted }, "\u6682\u65E0 skill\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA skill\u300D\u521B\u5EFA\u4E00\u4E2A\u3002") : (0, import_react3.createElement)(
5472
- "div",
5473
- { style: styles.section },
5474
- ...skills.map((sk) => (0, import_react3.createElement)(
5475
- "div",
5476
- {
5477
- key: sk.name,
5478
- style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--dsw-alias-border-l2)" }
5479
- },
5480
- (0, import_react3.createElement)(
5481
- "div",
5482
- { style: { display: "flex", flexDirection: "column", gap: 2, minWidth: 0 } },
5483
- (0, import_react3.createElement)(
5484
- "span",
5485
- { style: { fontSize: 13, color: "var(--dsw-alias-label-primary)" } },
5486
- sk.name,
5487
- sk.source !== "bundled" ? (0, import_react3.createElement)("span", { style: styles.sourceBadge }, `\uFF08${sourceLabel(sk.source)}\uFF09`) : null
5488
- ),
5489
- (0, import_react3.createElement)("span", { style: styles.sourceBadge }, sk.description)
5490
- ),
5491
- sk.source === "bundled" ? (0, import_react3.createElement)("span", { style: styles.sourceBadge }, "\u5185\u7F6E \xB7 \u53EA\u8BFB") : (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "sm", onClick: () => {
5492
- startEdit(sk);
5493
- } }, "\u7F16\u8F91")
5494
- ))
5495
- )
5496
- )
5177
+ "span",
5178
+ { style: { fontSize: 13, color: "var(--dsw-alias-label-primary)" } },
5179
+ sk.name,
5180
+ sk.source !== "bundled" ? (0, import_react3.createElement)("span", { style: styles.sourceBadge }, `\uFF08${sourceLabel(sk.source)}\uFF09`) : null
5181
+ ),
5182
+ (0, import_react3.createElement)("span", { style: styles.sourceBadge }, sk.description)
5497
5183
  ),
5498
- formOpen ? (0, import_react3.createElement)(
5184
+ sk.source === "bundled" ? (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "outline", size: "sm", onClick: () => {
5185
+ startView(sk);
5186
+ } }, "\u67E5\u770B") : (0, import_react3.createElement)(
5499
5187
  "div",
5500
- { style: card },
5501
- (0, import_react3.createElement)("span", { style: styles.bindingLabel }, editing === null ? "\u65B0\u5EFA skill" : `\u7F16\u8F91 skill\u300C${editing}\u300D`),
5188
+ { style: { display: "flex", gap: 6 } },
5189
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "outline", size: "sm", onClick: () => {
5190
+ startEdit(sk);
5191
+ } }, "\u7F16\u8F91"),
5192
+ confirming === sk.name ? (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "primary", size: "sm", onClick: () => {
5193
+ void remove(sk);
5194
+ } }, "\u786E\u8BA4\u5220\u9664") : (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: () => {
5195
+ setConfirming(sk.name);
5196
+ } }, "\u5220\u9664")
5197
+ )
5198
+ );
5199
+ const renderForm = () => {
5200
+ if (viewing) {
5201
+ return (0, import_react3.createElement)(
5202
+ ResourceModal,
5203
+ {
5204
+ title: `\u67E5\u770B skill\u300C${editing ?? ""}\u300D`,
5205
+ description: "\u5185\u7F6E \xB7 \u53EA\u8BFB" + (whenToUse !== "" && whenToUse !== void 0 ? ` \xB7 \u4F7F\u7528\u65F6\u673A\uFF1A${whenToUse}` : ""),
5206
+ onClose: closeForm,
5207
+ footer: (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: closeForm }, "\u5173\u95ED")
5208
+ },
5209
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.MarkdownText, { text: body2, labels: MD_LABELS })
5210
+ );
5211
+ }
5212
+ return (0, import_react3.createElement)(
5213
+ ResourceModal,
5214
+ {
5215
+ title: editing === null ? "\u65B0\u5EFA skill" : `\u7F16\u8F91 skill\u300C${editing ?? ""}\u300D`,
5216
+ onClose: closeForm,
5217
+ footer: (0, import_react3.createElement)(
5218
+ "div",
5219
+ { style: styles.row },
5220
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, {
5221
+ variant: "primary",
5222
+ size: "sm",
5223
+ disabled: editing === null && name.trim() === "" || description.trim() === "" || body2.trim() === "",
5224
+ onClick: () => {
5225
+ void submit();
5226
+ }
5227
+ }, "\u4FDD\u5B58"),
5228
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: closeForm }, "\u53D6\u6D88")
5229
+ )
5230
+ },
5502
5231
  (0, import_react3.createElement)(
5503
5232
  "div",
5504
5233
  { style: styles.chips },
5505
- (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Pill, { active: level === "project", onClick: () => {
5234
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Pill, { active: level === "project", onClick: () => {
5506
5235
  setLevel("project");
5507
5236
  }, title: "\u5199\u5230\u5DE5\u4F5C\u533A .dsh/skills\uFF0C\u56E2\u961F\u5171\u4EAB" }, "\u9879\u76EE\u7EA7"),
5508
- (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Pill, { active: level === "user", onClick: () => {
5237
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Pill, { active: level === "user", onClick: () => {
5509
5238
  setLevel("user");
5510
5239
  }, title: "\u5199\u5230 $DSH_HOME/skills\uFF0C\u4E2A\u4EBA\u6240\u6709\u9879\u76EE\u53EF\u7528" }, "\u7528\u6237\u7EA7")
5511
5240
  ),
5512
- editing === null ? (0, import_react3.createElement)("input", { style: control2, placeholder: "name\uFF08\u5982 my-skill\uFF0C\u53EA\u542B\u5C0F\u5199\u5B57\u6BCD\u6570\u5B57\u548C -\uFF09", value: name, onChange: (ev) => {
5241
+ editing === null ? (0, import_react3.createElement)("input", { style: control, placeholder: "name\uFF08\u5982 my-skill\uFF0C\u53EA\u542B\u5C0F\u5199\u5B57\u6BCD\u6570\u5B57\u548C -\uFF09", value: name, onChange: (ev) => {
5513
5242
  setName(ev.target.value);
5514
5243
  } }) : (0, import_react3.createElement)("span", { style: styles.sourceBadge }, `name \u56FA\u5B9A\u4E3A ${name}\uFF08\u6539\u540D\u8BF7\u5220\u9664\u540E\u91CD\u5EFA\uFF09`),
5515
- (0, import_react3.createElement)("input", { style: control2, placeholder: "description\uFF08\u4E00\u53E5\u8BDD\u8BF4\u660E\u5B83\u505A\u4EC0\u4E48\uFF09", value: description, onChange: (ev) => {
5244
+ (0, import_react3.createElement)("input", { style: control, placeholder: "description\uFF08\u4E00\u53E5\u8BDD\u8BF4\u660E\u5B83\u505A\u4EC0\u4E48\uFF09", value: description, onChange: (ev) => {
5516
5245
  setDescription(ev.target.value);
5517
5246
  } }),
5518
- (0, import_react3.createElement)("input", { style: control2, placeholder: "whenToUse\uFF08\u53EF\u9009\uFF1A\u4EC0\u4E48\u60C5\u51B5\u8BE5\u7528\uFF09", value: whenToUse, onChange: (ev) => {
5247
+ (0, import_react3.createElement)("input", { style: control, placeholder: "whenToUse\uFF08\u53EF\u9009\uFF1A\u4EC0\u4E48\u60C5\u51B5\u8BE5\u7528\uFF09", value: whenToUse, onChange: (ev) => {
5519
5248
  setWhenToUse(ev.target.value);
5520
5249
  } }),
5521
- (0, import_react3.createElement)("textarea", { style: { ...styles.textarea, minHeight: 220 }, placeholder: "\u6B63\u6587\uFF08\u544A\u8BC9\u5927\u6A21\u578B\u8FD9\u4E2A\u9636\u6BB5\u600E\u4E48\u5E72\uFF09", value: body2, onChange: (ev) => {
5250
+ (0, import_react3.createElement)("textarea", { style: { ...styles.textarea, minHeight: 300 }, placeholder: "\u6B63\u6587\uFF08\u544A\u8BC9\u5927\u6A21\u578B\u8FD9\u4E2A\u9636\u6BB5\u600E\u4E48\u5E72\uFF09", value: body2, onChange: (ev) => {
5522
5251
  setBody(ev.target.value);
5523
5252
  } }),
5253
+ msg !== "" ? (0, import_react3.createElement)("p", { style: styles.status }, msg) : null
5254
+ );
5255
+ };
5256
+ return (0, import_react3.createElement)(
5257
+ "div",
5258
+ { style: styles.section },
5259
+ (0, import_react3.createElement)(
5260
+ "div",
5261
+ { style: card },
5524
5262
  (0, import_react3.createElement)(
5525
5263
  "div",
5526
- { style: styles.row },
5527
- (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Button, {
5528
- variant: "primary",
5529
- size: "sm",
5530
- disabled: editing === null && name.trim() === "" || description.trim() === "" || body2.trim() === "",
5531
- onClick: () => {
5532
- void submit();
5533
- }
5534
- }, "\u4FDD\u5B58"),
5535
- (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Button, { variant: "ghost", size: "sm", onClick: () => {
5536
- setEditing(null);
5537
- setCreating(false);
5538
- setName("");
5539
- setDescription("");
5540
- setWhenToUse("");
5541
- setBody("");
5542
- setMsg("");
5543
- } }, "\u53D6\u6D88")
5264
+ { style: styles.bindingStage },
5265
+ (0, import_react3.createElement)("span", { style: styles.bindingLabel }, "\u5DF2\u5B89\u88C5\u7684 skill"),
5266
+ skills.length === 0 ? (0, import_react3.createElement)("p", { style: styles.muted }, "\u6682\u65E0 skill\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA skill\u300D\u521B\u5EFA\u4E00\u4E2A\u3002") : (0, import_react3.createElement)(
5267
+ "div",
5268
+ { style: styles.section },
5269
+ ...skills.map(renderRow)
5270
+ )
5544
5271
  ),
5545
- msg !== "" ? (0, import_react3.createElement)("p", { style: styles.status }, msg) : null
5546
- ) : (0, import_react3.createElement)(
5547
- "div",
5548
- null,
5549
- (0, import_react3.createElement)(import_dsh_client_ui_primitives2.Button, { variant: "outline", size: "md", onClick: startCreate }, "\u65B0\u5EFA skill")
5550
- )
5272
+ (0, import_react3.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "outline", size: "md", onClick: startCreate }, "\u65B0\u5EFA skill")
5273
+ ),
5274
+ formOpen ? renderForm() : null
5551
5275
  );
5552
5276
  }
5553
5277
 
5554
5278
  // src/client/RuleManager.tsx
5555
5279
  var import_react4 = require("react");
5556
- var import_dsh_client_ui_primitives3 = require("@deepseek-ai/dsh-client-ui-primitives");
5557
- var control3 = {
5280
+ var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
5281
+ var MD_LABELS2 = { code: { copyLabel: "\u590D\u5236", copiedLabel: "\u5DF2\u590D\u5236" }, footnotes: "\u811A\u6CE8" };
5282
+ var control2 = {
5558
5283
  background: "var(--dsw-alias-bg-layer-1)",
5559
5284
  border: "1px solid var(--dsw-alias-border-l2)",
5560
5285
  borderRadius: 8,
@@ -5572,6 +5297,7 @@ var card2 = {
5572
5297
  padding: "10px 12px"
5573
5298
  };
5574
5299
  function levelOf2(source) {
5300
+ if (source === "bundled") return "bundled";
5575
5301
  return source.startsWith("user") ? "user" : "project";
5576
5302
  }
5577
5303
  function RuleManager({ workspace, remote }) {
@@ -5579,6 +5305,8 @@ function RuleManager({ workspace, remote }) {
5579
5305
  const [level, setLevel] = (0, import_react4.useState)("project");
5580
5306
  const [editing, setEditing] = (0, import_react4.useState)(null);
5581
5307
  const [creating, setCreating] = (0, import_react4.useState)(false);
5308
+ const [viewing, setViewing] = (0, import_react4.useState)(false);
5309
+ const [confirming, setConfirming] = (0, import_react4.useState)(null);
5582
5310
  const [name, setName] = (0, import_react4.useState)("");
5583
5311
  const [body2, setBody] = (0, import_react4.useState)("");
5584
5312
  const [msg, setMsg] = (0, import_react4.useState)("");
@@ -5591,21 +5319,26 @@ function RuleManager({ workspace, remote }) {
5591
5319
  (0, import_react4.useEffect)(() => {
5592
5320
  refresh();
5593
5321
  }, [refresh]);
5322
+ const closeForm = () => {
5323
+ setEditing(null);
5324
+ setCreating(false);
5325
+ setViewing(false);
5326
+ setName("");
5327
+ setBody("");
5328
+ setMsg("");
5329
+ };
5594
5330
  const startCreate = () => {
5595
5331
  setEditing(null);
5596
5332
  setCreating(true);
5333
+ setViewing(false);
5597
5334
  setLevel("project");
5598
5335
  setName("");
5599
5336
  setBody("");
5600
5337
  setMsg("");
5601
5338
  };
5602
- const startEdit = (rule) => {
5603
- const lvl = levelOf2(rule.source);
5604
- setEditing(rule.name);
5605
- setCreating(false);
5606
- setLevel(lvl);
5339
+ const loadRule = (ruleName, lvl) => {
5607
5340
  setMsg("");
5608
- void remote.readRule({ name: rule.name, level: lvl, path: workspace }).then((r) => {
5341
+ void remote.readRule({ name: ruleName, level: lvl, path: workspace }).then((r) => {
5609
5342
  if (r.ok && r.value.ok) {
5610
5343
  setName(r.value.name);
5611
5344
  setBody(r.value.content);
@@ -5614,6 +5347,20 @@ function RuleManager({ workspace, remote }) {
5614
5347
  }
5615
5348
  });
5616
5349
  };
5350
+ const startEdit = (rule) => {
5351
+ const lvl = levelOf2(rule.source);
5352
+ setEditing(rule.name);
5353
+ setCreating(false);
5354
+ setViewing(false);
5355
+ setLevel(lvl === "user" ? "user" : "project");
5356
+ loadRule(rule.name, lvl);
5357
+ };
5358
+ const startView = (rule) => {
5359
+ setEditing(rule.name);
5360
+ setCreating(false);
5361
+ setViewing(true);
5362
+ loadRule(rule.name, "bundled");
5363
+ };
5617
5364
  const submit = async () => {
5618
5365
  setMsg("");
5619
5366
  const result = await remote.writeRule({
@@ -5635,90 +5382,130 @@ function RuleManager({ workspace, remote }) {
5635
5382
  setCreating(false);
5636
5383
  refresh();
5637
5384
  };
5385
+ const remove = async (rule) => {
5386
+ setMsg("");
5387
+ const lvl = levelOf2(rule.source);
5388
+ if (lvl === "bundled") return;
5389
+ const result = await remote.deleteRule({ name: rule.name, level: lvl, path: workspace });
5390
+ if (!result.ok) {
5391
+ setMsg("\u5220\u9664\u5931\u8D25\uFF1A" + describeError(result.error));
5392
+ setConfirming(null);
5393
+ return;
5394
+ }
5395
+ if (!result.value.ok) {
5396
+ setMsg("\u5220\u9664\u5931\u8D25\uFF1A" + (result.value.error ?? "\u672A\u77E5\u9519\u8BEF"));
5397
+ setConfirming(null);
5398
+ return;
5399
+ }
5400
+ setMsg(`\u5DF2\u5220\u9664 rule\u300C${result.value.name}\u300D`);
5401
+ if (editing === rule.name) setEditing(null);
5402
+ setConfirming(null);
5403
+ refresh();
5404
+ };
5638
5405
  const formOpen = editing !== null || creating;
5639
- return (0, import_react4.createElement)(
5406
+ const renderRow = (rule) => (0, import_react4.createElement)(
5640
5407
  "div",
5641
- { style: styles.section },
5408
+ {
5409
+ key: rule.name,
5410
+ style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--dsw-alias-border-l2)" }
5411
+ },
5642
5412
  (0, import_react4.createElement)(
5413
+ "span",
5414
+ { style: { fontSize: 13, color: "var(--dsw-alias-label-primary)" } },
5415
+ rule.name,
5416
+ rule.source !== "bundled" ? (0, import_react4.createElement)("span", { style: styles.sourceBadge }, `\uFF08${sourceLabel(rule.source)}\uFF09`) : null
5417
+ ),
5418
+ rule.source === "bundled" ? (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "sm", onClick: () => {
5419
+ startView(rule);
5420
+ } }, "\u67E5\u770B") : (0, import_react4.createElement)(
5643
5421
  "div",
5644
- { style: card2 },
5645
- (0, import_react4.createElement)(
5646
- "div",
5647
- { style: styles.bindingStage },
5648
- (0, import_react4.createElement)("span", { style: styles.bindingLabel }, "\u5DF2\u5B89\u88C5\u7684 rule"),
5649
- rules.length === 0 ? (0, import_react4.createElement)("p", { style: styles.muted }, "\u6682\u65E0 rule\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA rule\u300D\u521B\u5EFA\u4E00\u4E2A\u3002") : (0, import_react4.createElement)(
5422
+ { style: { display: "flex", gap: 6 } },
5423
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "sm", onClick: () => {
5424
+ startEdit(rule);
5425
+ } }, "\u7F16\u8F91"),
5426
+ confirming === rule.name ? (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "primary", size: "sm", onClick: () => {
5427
+ void remove(rule);
5428
+ } }, "\u786E\u8BA4\u5220\u9664") : (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: () => {
5429
+ setConfirming(rule.name);
5430
+ } }, "\u5220\u9664")
5431
+ )
5432
+ );
5433
+ const renderForm = () => {
5434
+ if (viewing) {
5435
+ return (0, import_react4.createElement)(
5436
+ ResourceModal,
5437
+ {
5438
+ title: `\u67E5\u770B rule\u300C${editing ?? ""}\u300D`,
5439
+ description: "\u5185\u7F6E \xB7 \u53EA\u8BFB",
5440
+ onClose: closeForm,
5441
+ footer: (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: closeForm }, "\u5173\u95ED")
5442
+ },
5443
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.MarkdownText, { text: body2, labels: MD_LABELS2 })
5444
+ );
5445
+ }
5446
+ return (0, import_react4.createElement)(
5447
+ ResourceModal,
5448
+ {
5449
+ title: editing === null ? "\u65B0\u5EFA rule" : `\u7F16\u8F91 rule\u300C${editing ?? ""}\u300D`,
5450
+ onClose: closeForm,
5451
+ footer: (0, import_react4.createElement)(
5650
5452
  "div",
5651
- { style: styles.section },
5652
- ...rules.map((rule) => (0, import_react4.createElement)(
5653
- "div",
5654
- {
5655
- key: rule.name,
5656
- style: { display: "flex", alignItems: "center", gap: 8, justifyContent: "space-between", padding: "6px 0", borderBottom: "1px solid var(--dsw-alias-border-l2)" }
5657
- },
5658
- (0, import_react4.createElement)(
5659
- "span",
5660
- { style: { fontSize: 13, color: "var(--dsw-alias-label-primary)" } },
5661
- rule.name,
5662
- rule.source !== "bundled" ? (0, import_react4.createElement)("span", { style: styles.sourceBadge }, `\uFF08${sourceLabel(rule.source)}\uFF09`) : null
5663
- ),
5664
- rule.source === "bundled" ? (0, import_react4.createElement)("span", { style: styles.sourceBadge }, "\u5185\u7F6E \xB7 \u53EA\u8BFB") : (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "outline", size: "sm", onClick: () => {
5665
- startEdit(rule);
5666
- } }, "\u7F16\u8F91")
5667
- ))
5453
+ { style: styles.row },
5454
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, {
5455
+ variant: "primary",
5456
+ size: "sm",
5457
+ disabled: editing === null && name.trim() === "" || body2.trim() === "",
5458
+ onClick: () => {
5459
+ void submit();
5460
+ }
5461
+ }, "\u4FDD\u5B58"),
5462
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "ghost", size: "sm", onClick: closeForm }, "\u53D6\u6D88")
5668
5463
  )
5669
- )
5670
- ),
5671
- formOpen ? (0, import_react4.createElement)(
5672
- "div",
5673
- { style: card2 },
5674
- (0, import_react4.createElement)("span", { style: styles.bindingLabel }, editing === null ? "\u65B0\u5EFA rule" : `\u7F16\u8F91 rule\u300C${editing}\u300D`),
5464
+ },
5675
5465
  (0, import_react4.createElement)(
5676
5466
  "div",
5677
5467
  { style: styles.chips },
5678
- (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Pill, { active: level === "project", onClick: () => {
5468
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Pill, { active: level === "project", onClick: () => {
5679
5469
  setLevel("project");
5680
5470
  }, title: "\u5199\u5230\u5DE5\u4F5C\u533A .dsh/rules\uFF0C\u56E2\u961F\u5171\u4EAB" }, "\u9879\u76EE\u7EA7"),
5681
- (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Pill, { active: level === "user", onClick: () => {
5471
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Pill, { active: level === "user", onClick: () => {
5682
5472
  setLevel("user");
5683
5473
  }, title: "\u5199\u5230 $DSH_HOME/rules\uFF0C\u4E2A\u4EBA\u6240\u6709\u9879\u76EE\u53EF\u7528" }, "\u7528\u6237\u7EA7")
5684
5474
  ),
5685
- editing === null ? (0, import_react4.createElement)("input", { style: control3, placeholder: "name\uFF08\u5982 my-redlines\uFF0C\u53EA\u542B\u5C0F\u5199\u5B57\u6BCD\u6570\u5B57\u548C -\uFF09", value: name, onChange: (ev) => {
5475
+ editing === null ? (0, import_react4.createElement)("input", { style: control2, placeholder: "name\uFF08\u5982 my-redlines\uFF0C\u53EA\u542B\u5C0F\u5199\u5B57\u6BCD\u6570\u5B57\u548C -\uFF09", value: name, onChange: (ev) => {
5686
5476
  setName(ev.target.value);
5687
5477
  } }) : (0, import_react4.createElement)("span", { style: styles.sourceBadge }, `name \u56FA\u5B9A\u4E3A ${name}\uFF08\u6539\u540D\u8BF7\u5220\u9664\u540E\u91CD\u5EFA\uFF09`),
5688
- (0, import_react4.createElement)("textarea", { style: { ...styles.textarea, minHeight: 240 }, placeholder: "\u6B63\u6587\uFF08\u8FD9\u6761\u89C4\u5219\u7EA6\u675F\u5927\u6A21\u578B\u9075\u5B88\u4EC0\u4E48\uFF09", value: body2, onChange: (ev) => {
5478
+ (0, import_react4.createElement)("textarea", { style: { ...styles.textarea, minHeight: 260 }, placeholder: "\u6B63\u6587\uFF08\u8FD9\u6761\u89C4\u5219\u7EA6\u675F\u5927\u6A21\u578B\u9075\u5B88\u4EC0\u4E48\uFF09", value: body2, onChange: (ev) => {
5689
5479
  setBody(ev.target.value);
5690
5480
  } }),
5481
+ msg !== "" ? (0, import_react4.createElement)("p", { style: styles.status }, msg) : null
5482
+ );
5483
+ };
5484
+ return (0, import_react4.createElement)(
5485
+ "div",
5486
+ { style: styles.section },
5487
+ (0, import_react4.createElement)(
5488
+ "div",
5489
+ { style: card2 },
5691
5490
  (0, import_react4.createElement)(
5692
5491
  "div",
5693
- { style: styles.row },
5694
- (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Button, {
5695
- variant: "primary",
5696
- size: "sm",
5697
- disabled: editing === null && name.trim() === "" || body2.trim() === "",
5698
- onClick: () => {
5699
- void submit();
5700
- }
5701
- }, "\u4FDD\u5B58"),
5702
- (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "ghost", size: "sm", onClick: () => {
5703
- setEditing(null);
5704
- setCreating(false);
5705
- setName("");
5706
- setBody("");
5707
- setMsg("");
5708
- } }, "\u53D6\u6D88")
5492
+ { style: styles.bindingStage },
5493
+ (0, import_react4.createElement)("span", { style: styles.bindingLabel }, "\u5DF2\u5B89\u88C5\u7684 rule"),
5494
+ rules.length === 0 ? (0, import_react4.createElement)("p", { style: styles.muted }, "\u6682\u65E0 rule\uFF0C\u70B9\u4E0B\u65B9\u300C\u65B0\u5EFA rule\u300D\u521B\u5EFA\u4E00\u4E2A\u3002") : (0, import_react4.createElement)(
5495
+ "div",
5496
+ { style: styles.section },
5497
+ ...rules.map(renderRow)
5498
+ )
5709
5499
  ),
5710
- msg !== "" ? (0, import_react4.createElement)("p", { style: styles.status }, msg) : null
5711
- ) : (0, import_react4.createElement)(
5712
- "div",
5713
- null,
5714
- (0, import_react4.createElement)(import_dsh_client_ui_primitives3.Button, { variant: "outline", size: "md", onClick: startCreate }, "\u65B0\u5EFA rule")
5715
- )
5500
+ (0, import_react4.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "md", onClick: startCreate }, "\u65B0\u5EFA rule")
5501
+ ),
5502
+ formOpen ? renderForm() : null
5716
5503
  );
5717
5504
  }
5718
5505
 
5719
5506
  // src/client/TaskLedger.tsx
5720
5507
  var import_react5 = require("react");
5721
- var import_dsh_client_ui_primitives4 = require("@deepseek-ai/dsh-client-ui-primitives");
5508
+ var import_dsh_client_ui_primitives5 = require("@deepseek-ai/dsh-client-ui-primitives");
5722
5509
  var card3 = {
5723
5510
  display: "flex",
5724
5511
  flexDirection: "column",
@@ -5756,10 +5543,10 @@ var badge = {
5756
5543
  color: "var(--dsw-alias-label-secondary)"
5757
5544
  };
5758
5545
  function verdictDot(outcome) {
5759
- return (0, import_react5.createElement)(import_dsh_client_ui_primitives4.StateDot, { state: outcome === "pass" ? "done" : "error" });
5546
+ return (0, import_react5.createElement)(import_dsh_client_ui_primitives5.StateDot, { state: outcome === "pass" ? "done" : "error" });
5760
5547
  }
5761
5548
  function itemStatusDot(status) {
5762
- return (0, import_react5.createElement)(import_dsh_client_ui_primitives4.StateDot, { state: status === "done" ? "done" : "ongoing" });
5549
+ return (0, import_react5.createElement)(import_dsh_client_ui_primitives5.StateDot, { state: status === "done" ? "done" : "ongoing" });
5763
5550
  }
5764
5551
  function timeText(at) {
5765
5552
  return at.slice(0, 19).replace("T", " ");
@@ -5825,7 +5612,7 @@ function TaskLedger({ workspace, remote }) {
5825
5612
  { style: headerRow },
5826
5613
  (0, import_react5.createElement)("span", { style: styles.bindingLabel }, "\u4EFB\u52A1\u53F0\u8D26"),
5827
5614
  (0, import_react5.createElement)("span", { style: styles.sourceBadge }, "\u53EA\u8BFB \xB7 \u6765\u81EA .dsh/task-*.json"),
5828
- (0, import_react5.createElement)(import_dsh_client_ui_primitives4.Button, { variant: "outline", size: "sm", onClick: refresh }, "\u5237\u65B0")
5615
+ (0, import_react5.createElement)(import_dsh_client_ui_primitives5.Button, { variant: "outline", size: "sm", onClick: refresh }, "\u5237\u65B0")
5829
5616
  ),
5830
5617
  error !== "" ? (0, import_react5.createElement)("p", { style: styles.status }, error) : tasks.length === 0 ? (0, import_react5.createElement)("p", { style: styles.muted }, "\u5F53\u524D\u5DE5\u4F5C\u533A\u8FD8\u6CA1\u6709\u4EFB\u52A1\uFF1A\u5F00\u5DE5\u540E\uFF08dev_task create\uFF09\u4F1A\u5728\u8FD9\u91CC\u5217\u51FA\u6D3E\u53D1\u4E0E\u5BA1\u67E5\u7559\u75D5\u3002") : (0, import_react5.createElement)(
5831
5618
  "div",
@@ -5936,13 +5723,13 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
5936
5723
  { type: "button", title: "\u5173\u95ED", onClick: () => {
5937
5724
  actions.close();
5938
5725
  }, style: closeStyle },
5939
- (0, import_react6.createElement)(import_dsh_client_ui_primitives5.IconCloseOutline16, { size: 16 })
5726
+ (0, import_react6.createElement)(import_dsh_client_ui_primitives6.IconCloseOutline16, { size: 16 })
5940
5727
  )
5941
5728
  ),
5942
5729
  (0, import_react6.createElement)(
5943
5730
  "div",
5944
5731
  { style: tabbar },
5945
- ...TABS.map((t) => (0, import_react6.createElement)(import_dsh_client_ui_primitives5.Pill, { key: t.id, active: tab === t.id, onClick: () => {
5732
+ ...TABS.map((t) => (0, import_react6.createElement)(import_dsh_client_ui_primitives6.Pill, { key: t.id, active: tab === t.id, onClick: () => {
5946
5733
  setTab(t.id);
5947
5734
  } }, t.label)),
5948
5735
  workspaces.length > 0 ? (0, import_react6.createElement)("select", {
@@ -5963,7 +5750,7 @@ function Workbench({ useStore, actions, useWorkspaces, remote }) {
5963
5750
 
5964
5751
  // src/client/TriggerButton.tsx
5965
5752
  var import_react7 = require("react");
5966
- var import_dsh_client_ui_primitives6 = require("@deepseek-ai/dsh-client-ui-primitives");
5753
+ var import_dsh_client_ui_primitives7 = require("@deepseek-ai/dsh-client-ui-primitives");
5967
5754
  function TriggerButton({ wide, actions }) {
5968
5755
  return (0, import_react7.createElement)(
5969
5756
  "button",
@@ -5990,7 +5777,7 @@ function TriggerButton({ wide, actions }) {
5990
5777
  boxSizing: "border-box"
5991
5778
  }
5992
5779
  },
5993
- (0, import_react7.createElement)(import_dsh_client_ui_primitives6.IconSettingsOutline16, { size: wide ? 16 : 18 }),
5780
+ (0, import_react7.createElement)(import_dsh_client_ui_primitives7.IconSettingsOutline16, { size: wide ? 16 : 18 }),
5994
5781
  wide ? (0, import_react7.createElement)("span", { style: { fontSize: 13 } }, "\u5DE5\u7A0B\u6D41\u7A0B") : null
5995
5782
  );
5996
5783
  }