@godv61/dsh-task-engine 0.5.2 → 0.6.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
@@ -4088,13 +4088,18 @@ var commitSchema = external_exports.object({
4088
4088
  checkpoints: external_exports.array(external_exports.string()),
4089
4089
  file_scope: external_exports.boolean()
4090
4090
  });
4091
+ var stageBindingSchema = external_exports.object({
4092
+ skills: external_exports.array(external_exports.string()).optional(),
4093
+ rules: external_exports.array(external_exports.string()).optional()
4094
+ });
4091
4095
  var configSchema = external_exports.object({
4092
4096
  stages: external_exports.array(external_exports.string()),
4093
4097
  start_stage: external_exports.string(),
4094
4098
  transitions: external_exports.array(transitionSchema),
4095
4099
  artifacts: external_exports.array(artifactSchema),
4096
4100
  commit: commitSchema,
4097
- high_risk_requires_verification: external_exports.boolean()
4101
+ high_risk_requires_verification: external_exports.boolean(),
4102
+ stage_bindings: external_exports.record(external_exports.string(), stageBindingSchema).optional()
4098
4103
  });
4099
4104
  var viewSchema = external_exports.object({
4100
4105
  ok: external_exports.boolean(),
@@ -4106,6 +4111,39 @@ var writeRequestSchema = external_exports.object({
4106
4111
  path: external_exports.string(),
4107
4112
  config: configSchema
4108
4113
  });
4114
+ var skillCatalogSchema = external_exports.object({
4115
+ skills: external_exports.array(external_exports.object({
4116
+ name: external_exports.string(),
4117
+ description: external_exports.string(),
4118
+ source: external_exports.string()
4119
+ }))
4120
+ });
4121
+ var ruleCatalogSchema = external_exports.object({
4122
+ rules: external_exports.array(external_exports.object({
4123
+ name: external_exports.string(),
4124
+ source: external_exports.string()
4125
+ }))
4126
+ });
4127
+ var writeSkillRequestSchema = external_exports.object({
4128
+ name: external_exports.string(),
4129
+ description: external_exports.string(),
4130
+ whenToUse: external_exports.string().optional(),
4131
+ content: external_exports.string(),
4132
+ level: external_exports.enum(["project", "user"]),
4133
+ path: external_exports.string().optional()
4134
+ });
4135
+ var writeRuleRequestSchema = external_exports.object({
4136
+ name: external_exports.string(),
4137
+ content: external_exports.string(),
4138
+ level: external_exports.enum(["project", "user"]),
4139
+ path: external_exports.string().optional()
4140
+ });
4141
+ var writeResourceResultSchema = external_exports.object({
4142
+ ok: external_exports.boolean(),
4143
+ name: external_exports.string(),
4144
+ path: external_exports.string(),
4145
+ error: external_exports.string().optional()
4146
+ });
4109
4147
  var PACKAGE = "@godv61/dsh-task-engine";
4110
4148
  var TYPERT_REMOTE = {
4111
4149
  package: PACKAGE,
@@ -4141,6 +4179,70 @@ var TYPERT_REMOTE = {
4141
4179
  }
4142
4180
  ],
4143
4181
  result: { mode: "strict", typeSymbol: `${PACKAGE}/types#EngConfigView`, schema: viewSchema }
4182
+ },
4183
+ {
4184
+ id: `${PACKAGE}#task-engine/listSkills`,
4185
+ service: "taskEngineController",
4186
+ namespace: "task-engine",
4187
+ method: "listSkills",
4188
+ invocation: { kind: "direct" },
4189
+ parameters: [
4190
+ {
4191
+ name: "path",
4192
+ wire: "path",
4193
+ source: "json",
4194
+ codec: { mode: "strict", typeSymbol: "string", schema: external_exports.string() }
4195
+ }
4196
+ ],
4197
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#SkillCatalog`, schema: skillCatalogSchema }
4198
+ },
4199
+ {
4200
+ id: `${PACKAGE}#task-engine/listRules`,
4201
+ service: "taskEngineController",
4202
+ namespace: "task-engine",
4203
+ method: "listRules",
4204
+ invocation: { kind: "direct" },
4205
+ parameters: [
4206
+ {
4207
+ name: "path",
4208
+ wire: "path",
4209
+ source: "json",
4210
+ codec: { mode: "strict", typeSymbol: "string", schema: external_exports.string() }
4211
+ }
4212
+ ],
4213
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#RuleCatalog`, schema: ruleCatalogSchema }
4214
+ },
4215
+ {
4216
+ id: `${PACKAGE}#task-engine/writeSkill`,
4217
+ service: "taskEngineController",
4218
+ namespace: "task-engine",
4219
+ method: "writeSkill",
4220
+ invocation: { kind: "direct" },
4221
+ parameters: [
4222
+ {
4223
+ name: "request",
4224
+ wire: "request",
4225
+ source: "json",
4226
+ codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteSkillRequest`, schema: writeSkillRequestSchema }
4227
+ }
4228
+ ],
4229
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
4230
+ },
4231
+ {
4232
+ id: `${PACKAGE}#task-engine/writeRule`,
4233
+ service: "taskEngineController",
4234
+ namespace: "task-engine",
4235
+ method: "writeRule",
4236
+ invocation: { kind: "direct" },
4237
+ parameters: [
4238
+ {
4239
+ name: "request",
4240
+ wire: "request",
4241
+ source: "json",
4242
+ codec: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteRuleRequest`, schema: writeRuleRequestSchema }
4243
+ }
4244
+ ],
4245
+ result: { mode: "strict", typeSymbol: `${PACKAGE}/types#WriteResourceResult`, schema: writeResourceResultSchema }
4144
4246
  }
4145
4247
  ]
4146
4248
  };
@@ -4206,6 +4308,22 @@ function validateWorkflow(config) {
4206
4308
  fieldSet.add(field);
4207
4309
  }
4208
4310
  });
4311
+ const bindings = config.stage_bindings ?? {};
4312
+ for (const [stage, binding] of Object.entries(bindings)) {
4313
+ if (!config.stages.includes(stage)) {
4314
+ problems.push(`stage_bindings: stage "${stage}" is not a stage`);
4315
+ }
4316
+ for (const skill of binding.skills ?? []) {
4317
+ if (typeof skill !== "string" || skill.trim() === "") {
4318
+ problems.push(`stage_bindings "${stage}": empty skill name`);
4319
+ }
4320
+ }
4321
+ for (const rule of binding.rules ?? []) {
4322
+ if (typeof rule !== "string" || rule.trim() === "") {
4323
+ problems.push(`stage_bindings "${stage}": empty rule name`);
4324
+ }
4325
+ }
4326
+ }
4209
4327
  if (!["task", "item", "manual"].includes(config.commit.policy)) {
4210
4328
  problems.push(`commit.policy "${config.commit.policy}" is not task|item|manual`);
4211
4329
  }
@@ -4248,7 +4366,10 @@ function edgeList(config) {
4248
4366
  function nodeX(index, stageCount) {
4249
4367
  return index < 0 ? margin + stageCount * stepX + nodeW : margin + index * stepX;
4250
4368
  }
4251
- function FlowDiagram({ config }) {
4369
+ function FlowDiagram({ config, onAddTransition, onRemoveTransition }) {
4370
+ const [dragFrom, setDragFrom] = (0, import_react.useState)(null);
4371
+ const [dragPos, setDragPos] = (0, import_react.useState)(null);
4372
+ const svgRef = (0, import_react.useRef)(null);
4252
4373
  const stages = config.stages;
4253
4374
  const edges = edgeList(config);
4254
4375
  const stageCount = stages.length;
@@ -4258,13 +4379,27 @@ function FlowDiagram({ config }) {
4258
4379
  };
4259
4380
  const width = Math.max(margin * 2 + stageCount * stepX, 360);
4260
4381
  const height = 210;
4261
- const nodeTitle = (stage) => `${stage}${stage === config.start_stage ? "\uFF08\u8D77\u59CB\uFF09" : ""}`;
4382
+ const toSvg = (e) => {
4383
+ const rect = svgRef.current.getBoundingClientRect();
4384
+ return { x: e.clientX - rect.left, y: e.clientY - rect.top };
4385
+ };
4386
+ const startDrag = (stage, e) => {
4387
+ setDragFrom(stage);
4388
+ setDragPos(toSvg(e));
4389
+ };
4390
+ const dropOn = (stage) => {
4391
+ if (dragFrom !== null && dragFrom !== stage) onAddTransition(dragFrom, stage);
4392
+ setDragFrom(null);
4393
+ setDragPos(null);
4394
+ };
4395
+ const cancelDrag = () => {
4396
+ setDragFrom(null);
4397
+ setDragPos(null);
4398
+ };
4262
4399
  const forwardPath = (from, to) => {
4263
4400
  const x1 = nodeX(from, stageCount) + nodeW;
4264
4401
  const x2 = nodeX(to, stageCount);
4265
- if (to === from + 1) {
4266
- return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4267
- }
4402
+ if (to === from + 1) return `M ${x1} ${nodeCenterY} L ${x2} ${nodeCenterY}`;
4268
4403
  const cx = (x1 + x2) / 2;
4269
4404
  const cy = nodeCenterY + (to - from) * 20;
4270
4405
  return `M ${x1} ${nodeCenterY} Q ${cx} ${cy} ${x2} ${nodeCenterY}`;
@@ -4281,18 +4416,38 @@ function FlowDiagram({ config }) {
4281
4416
  const cx = (x1 + x2) / 2;
4282
4417
  return `M ${x1} ${y1} Q ${cx} ${y1 - lift} ${x2} ${y1}`;
4283
4418
  };
4284
- const strokeOf = (edge) => edge.orphaned ? danger : textTertiary;
4285
4419
  const forwardLabelY = (from, to) => to === from + 1 ? nodeCenterY - 8 : nodeCenterY + (to - from) * 20 + 16;
4286
4420
  const returnLabelY = (from, to) => nodeTop - 30 - Math.abs(from - to) * 6;
4421
+ const dragLine = dragFrom !== null && dragPos !== null ? (() => {
4422
+ const from = indexOf(dragFrom);
4423
+ const cx = from < 0 ? nodeX(-1, stageCount) + nodeW / 2 : nodeX(from, stageCount) + nodeW / 2;
4424
+ const cy = nodeTop;
4425
+ return (0, import_react.createElement)("line", {
4426
+ x1: cx,
4427
+ y1: cy,
4428
+ x2: dragPos.x,
4429
+ y2: dragPos.y,
4430
+ stroke: brand,
4431
+ strokeWidth: 1.5,
4432
+ strokeDasharray: "5 3",
4433
+ pointerEvents: "none"
4434
+ });
4435
+ })() : null;
4287
4436
  return (0, import_react.createElement)(
4288
4437
  "svg",
4289
4438
  {
4439
+ ref: svgRef,
4290
4440
  width,
4291
4441
  height,
4292
4442
  viewBox: `0 0 ${width} ${height}`,
4293
4443
  style: { display: "block", minWidth: "100%", fontFamily: "inherit" },
4294
4444
  role: "img",
4295
- "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE"
4445
+ "aria-label": "\u5DE5\u7A0B\u9636\u6BB5\u6D41\u8F6C\u56FE\uFF08\u53EF\u62D6\u62FD\u8FDE\u7EBF\u3001\u70B9\u51FB\u7BAD\u5934\u5220\u9664\uFF09",
4446
+ onMouseMove: dragFrom === null ? void 0 : (e) => {
4447
+ setDragPos(toSvg(e));
4448
+ },
4449
+ onMouseUp: cancelDrag,
4450
+ onMouseLeave: cancelDrag
4296
4451
  },
4297
4452
  (0, import_react.createElement)(
4298
4453
  "defs",
@@ -4310,7 +4465,7 @@ function FlowDiagram({ config }) {
4310
4465
  ...edges.map((edge, i) => {
4311
4466
  const from = indexOf(edge.from);
4312
4467
  const to = indexOf(edge.to);
4313
- const stroke = strokeOf(edge);
4468
+ const stroke = edge.orphaned ? danger : textTertiary;
4314
4469
  const d = from < to ? forwardPath(from, to) : returnPath(from, to < 0 ? from : to);
4315
4470
  const labelY = from < to ? forwardLabelY(from, to) : returnLabelY(from, to < 0 ? from : to);
4316
4471
  const labelX = from < to ? (nodeX(from, stageCount) + nodeW + nodeX(to, stageCount)) / 2 : (nodeX(from, stageCount) + nodeX(to < 0 ? from : to, stageCount) + nodeW) / 2;
@@ -4321,6 +4476,10 @@ function FlowDiagram({ config }) {
4321
4476
  stroke,
4322
4477
  strokeWidth: 1.5,
4323
4478
  markerEnd: "url(#flow-arrow)",
4479
+ style: { cursor: "pointer" },
4480
+ onClick: () => {
4481
+ onRemoveTransition(i);
4482
+ },
4324
4483
  ...edge.orphaned ? { strokeDasharray: "4 3" } : {}
4325
4484
  };
4326
4485
  const label = edge.label === null ? null : (0, import_react.createElement)("text", {
@@ -4329,10 +4488,21 @@ function FlowDiagram({ config }) {
4329
4488
  textAnchor: "middle",
4330
4489
  fontSize: 11,
4331
4490
  fill: edge.orphaned ? danger : textSecondary
4332
- }, edge.from === edge.to ? edge.label : edge.label);
4491
+ }, edge.label);
4333
4492
  return (0, import_react.createElement)(
4334
4493
  "g",
4335
4494
  { key: `edgeg-${i}` },
4495
+ (0, import_react.createElement)(
4496
+ "path",
4497
+ {
4498
+ ...common,
4499
+ strokeWidth: 6,
4500
+ stroke: "transparent",
4501
+ fill: "none",
4502
+ markerEnd: void 0,
4503
+ strokeDasharray: void 0
4504
+ }
4505
+ ),
4336
4506
  (0, import_react.createElement)("path", common),
4337
4507
  label
4338
4508
  );
@@ -4342,7 +4512,16 @@ function FlowDiagram({ config }) {
4342
4512
  const isStart = stage === config.start_stage;
4343
4513
  return (0, import_react.createElement)(
4344
4514
  "g",
4345
- { key: stage },
4515
+ {
4516
+ key: stage,
4517
+ style: { cursor: dragFrom === null ? "crosshair" : "pointer" },
4518
+ onMouseDown: (e) => {
4519
+ startDrag(stage, e);
4520
+ },
4521
+ onMouseUp: () => {
4522
+ dropOn(stage);
4523
+ }
4524
+ },
4346
4525
  (0, import_react.createElement)("rect", {
4347
4526
  x,
4348
4527
  y: nodeTop,
@@ -4363,12 +4542,97 @@ function FlowDiagram({ config }) {
4363
4542
  fill: "var(--dsw-alias-label-primary)",
4364
4543
  fontWeight: isStart ? 600 : 400
4365
4544
  }, stage),
4366
- (0, import_react.createElement)("title", null, nodeTitle(stage))
4545
+ (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`)
4367
4546
  );
4368
- })
4547
+ }),
4548
+ dragLine
4369
4549
  );
4370
4550
  }
4371
4551
 
4552
+ // src/client/templates.ts
4553
+ var STANDARD = {
4554
+ stages: ["\u9700\u6C42\u8BC4\u5BA1", "\u8BBE\u8BA1", "\u5F00\u53D1", "\u4EA4\u4ED8", "\u4EE3\u7801\u5BA1\u6838", "\u5B8C\u6210"],
4555
+ start_stage: "\u9700\u6C42\u8BC4\u5BA1",
4556
+ transitions: [
4557
+ { from: "\u9700\u6C42\u8BC4\u5BA1", to: "\u8BBE\u8BA1", requires: ["requirement_confirmation", "artifacts_present"] },
4558
+ { from: "\u8BBE\u8BA1", to: "\u5F00\u53D1", requires: ["solution_confirmation", "artifacts_present"] },
4559
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] },
4560
+ { from: "\u4EA4\u4ED8", to: "\u4EE3\u7801\u5BA1\u6838", requires: ["verified"] },
4561
+ { from: "\u4EE3\u7801\u5BA1\u6838", to: "\u5B8C\u6210", requires: ["review_passed", "artifacts_present"] }
4562
+ ],
4563
+ artifacts: [
4564
+ { stage: "\u9700\u6C42\u8BC4\u5BA1", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope", "acceptance_criteria"] },
4565
+ { stage: "\u8BBE\u8BA1", id: "design", name: "\u8BBE\u8BA1\u6587\u6863", fields: ["approach", "risks", "impact"] },
4566
+ { stage: "\u4EE3\u7801\u5BA1\u6838", id: "review", name: "\u8BC4\u5BA1\u8BB0\u5F55", fields: ["conclusion", "issues"] }
4567
+ ],
4568
+ commit: {
4569
+ policy: "task",
4570
+ message_pattern: "^\u3010(\\S+)\u3011\u3010(?:TASK|T\\d+)\u3011.+",
4571
+ message_hint: "\u3010<module>\u3011\u3010TASK/T1\u3011\u8BF4\u660E \u2014\u2014 \u5199\u7ED3\u679C\uFF0C\u4E0D\u5199\u7A7A\u6CDB\u52A8\u4F5C",
4572
+ checkpoints: ["\u4EA4\u4ED8"],
4573
+ file_scope: true
4574
+ },
4575
+ high_risk_requires_verification: true,
4576
+ stage_bindings: {
4577
+ "\u9700\u6C42\u8BC4\u5BA1": { skills: ["requirement-analysis"], rules: ["security-redlines"] },
4578
+ "\u8BBE\u8BA1": { skills: ["solution-design"] },
4579
+ "\u5F00\u53D1": { skills: ["code-implement"], rules: ["coding-conventions"] },
4580
+ "\u4EA4\u4ED8": { skills: ["code-verify", "code-commit"], rules: ["coding-conventions", "commit-conventions"] },
4581
+ "\u4EE3\u7801\u5BA1\u6838": { skills: ["code-review"], rules: ["security-redlines"] }
4582
+ }
4583
+ };
4584
+ var AGILE = {
4585
+ stages: ["\u9700\u6C42", "\u5F00\u53D1", "\u4EA4\u4ED8", "\u5BA1\u67E5"],
4586
+ start_stage: "\u9700\u6C42",
4587
+ transitions: [
4588
+ { from: "\u9700\u6C42", to: "\u5F00\u53D1", requires: ["requirement_confirmation"] },
4589
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] },
4590
+ { from: "\u4EA4\u4ED8", to: "\u5BA1\u67E5", requires: [] }
4591
+ ],
4592
+ artifacts: [
4593
+ { stage: "\u9700\u6C42", id: "requirement", name: "\u9700\u6C42\u8BF4\u660E", fields: ["scope"] }
4594
+ ],
4595
+ commit: {
4596
+ policy: "item",
4597
+ message_pattern: "^\u3010(\\S+)\u3011\u3010T\\d+\u3011.+",
4598
+ message_hint: "\u3010<module>\u3011\u3010T1\u3011\u8BF4\u660E",
4599
+ checkpoints: ["\u4EA4\u4ED8"],
4600
+ file_scope: true
4601
+ },
4602
+ high_risk_requires_verification: false,
4603
+ stage_bindings: {
4604
+ "\u9700\u6C42": { skills: ["requirement-analysis"] },
4605
+ "\u5F00\u53D1": { skills: ["code-implement"], rules: ["coding-conventions"] },
4606
+ "\u4EA4\u4ED8": { skills: ["code-verify", "code-commit"], rules: ["commit-conventions"] },
4607
+ "\u5BA1\u67E5": { skills: ["code-review"] }
4608
+ }
4609
+ };
4610
+ var MINIMAL = {
4611
+ stages: ["\u5F00\u53D1", "\u4EA4\u4ED8"],
4612
+ start_stage: "\u5F00\u53D1",
4613
+ transitions: [
4614
+ { from: "\u5F00\u53D1", to: "\u4EA4\u4ED8", requires: ["todos_done"] }
4615
+ ],
4616
+ artifacts: [],
4617
+ commit: {
4618
+ policy: "task",
4619
+ message_pattern: "",
4620
+ message_hint: "\u65E0\u683C\u5F0F\u8981\u6C42",
4621
+ checkpoints: ["\u4EA4\u4ED8"],
4622
+ file_scope: false
4623
+ },
4624
+ high_risk_requires_verification: false,
4625
+ stage_bindings: {
4626
+ "\u5F00\u53D1": { skills: ["code-implement"] },
4627
+ "\u4EA4\u4ED8": { skills: ["code-commit"], rules: ["commit-conventions"] }
4628
+ }
4629
+ };
4630
+ var WORKFLOW_TEMPLATES = [
4631
+ { 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 },
4632
+ { 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 },
4633
+ { 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 }
4634
+ ];
4635
+
4372
4636
  // src/client/styles.ts
4373
4637
  var text = "var(--dsw-alias-label-primary)";
4374
4638
  var textSecondary2 = "var(--dsw-alias-label-secondary)";
@@ -4405,6 +4669,22 @@ var styles = {
4405
4669
  padding: "8px 10px"
4406
4670
  },
4407
4671
  inline: { display: "inline-flex", alignItems: "center", gap: 6, marginRight: 8 },
4672
+ textarea: {
4673
+ background: "var(--dsw-alias-bg-layer-1)",
4674
+ border: `1px solid ${border2}`,
4675
+ borderRadius: 8,
4676
+ padding: "8px 10px",
4677
+ color: text,
4678
+ fontSize: 13,
4679
+ minHeight: 90,
4680
+ resize: "vertical",
4681
+ fontFamily: "inherit",
4682
+ width: "100%",
4683
+ boxSizing: "border-box"
4684
+ },
4685
+ sourceBadge: { color: textTertiary2, fontSize: 11 },
4686
+ bindingStage: { display: "flex", flexDirection: "column", gap: 6 },
4687
+ bindingLabel: { color: text, fontSize: 13, fontWeight: 600 },
4408
4688
  problems: {
4409
4689
  display: "flex",
4410
4690
  flexDirection: "column",
@@ -4472,6 +4752,8 @@ function TaskEngineSection(props) {
4472
4752
  const [savedAt, setSavedAt] = (0, import_react2.useState)("");
4473
4753
  const [loadError, setLoadError] = (0, import_react2.useState)("");
4474
4754
  const [loadedProblems, setLoadedProblems] = (0, import_react2.useState)([]);
4755
+ const [skills, setSkills] = (0, import_react2.useState)([]);
4756
+ const [rules, setRules] = (0, import_react2.useState)([]);
4475
4757
  (0, import_react2.useEffect)(() => {
4476
4758
  if (workspaces.length > 0 && workspace === "") {
4477
4759
  setWorkspace(workspaces[0].path);
@@ -4498,8 +4780,33 @@ function TaskEngineSection(props) {
4498
4780
  alive = false;
4499
4781
  };
4500
4782
  }, [remote, workspace]);
4783
+ const refreshCatalogs = (0, import_react2.useCallback)(() => {
4784
+ if (workspace === "") return;
4785
+ void remote.listSkills(workspace).then((result) => {
4786
+ if (result.ok) setSkills(result.value.skills);
4787
+ }, () => {
4788
+ });
4789
+ void remote.listRules(workspace).then((result) => {
4790
+ if (result.ok) setRules(result.value.rules);
4791
+ }, () => {
4792
+ });
4793
+ }, [remote, workspace]);
4794
+ (0, import_react2.useEffect)(() => {
4795
+ refreshCatalogs();
4796
+ }, [refreshCatalogs]);
4501
4797
  const problems = (0, import_react2.useMemo)(() => config === null ? [] : validateWorkflow(config), [config]);
4502
4798
  const notice = problems.length === 0 && loadedProblems.length > 0 ? loadedProblems : [];
4799
+ const addTransition = (from, to) => {
4800
+ if (config === null || from === "" || to === "") return;
4801
+ update(config, setConfig, (c) => ({
4802
+ ...c,
4803
+ transitions: [...c.transitions, { from, to }]
4804
+ }));
4805
+ };
4806
+ const removeTransition = (index) => {
4807
+ if (config === null) return;
4808
+ update(config, setConfig, (c) => ({ ...c, transitions: c.transitions.filter((_, j) => j !== index) }));
4809
+ };
4503
4810
  if (workspaces.length === 0) {
4504
4811
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.wrap, children: [
4505
4812
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("h2", { style: styles.title, children: "\u5DE5\u7A0B\u6D41\u7A0B\u914D\u7F6E" }),
@@ -4547,11 +4854,14 @@ function TaskEngineSection(props) {
4547
4854
  }
4548
4855
  )
4549
4856
  ] }),
4550
- /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowCard, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlowDiagram, { config }) }),
4551
- /* @__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", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(StageEditor, { config, setConfig }) }),
4552
- /* @__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", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TransitionEditor, { config, setConfig }) }),
4553
- /* @__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\u586B\u7684\u5B57\u6BB5\uFF09", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactEditor, { config, setConfig }) }),
4554
- /* @__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", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(CommitEditor, { config, setConfig }) }),
4857
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(TemplatePicker, { config, setConfig }),
4858
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.flowCard, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(FlowDiagram, { config, onAddTransition: addTransition, onRemoveTransition: removeTransition }) }),
4859
+ /* @__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 }) }),
4860
+ /* @__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", 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 }) }),
4861
+ /* @__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\u586B\u7684\u5B57\u6BB5\uFF09", hint: "\u7ED9\u67D0\u9636\u6BB5\u58F0\u660E\u8981\u4EA4\u7684\u4EA7\u7269\u548C\u5FC5\u586B\u5B57\u6BB5\uFF1B\u914D\u5408\u6D41\u8F6C\u91CC\u7684\u300C\u4EA7\u7269\u9F50\u5168\u300D\u5B88\u536B\uFF0C\u5B57\u6BB5\u6CA1\u586B\u5168\u5C31\u8FC7\u4E0D\u53BB\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ArtifactEditor, { config, setConfig }) }),
4862
+ /* @__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", 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 }) }),
4863
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconBranchOutline16, { size: 16 }), title: "\u8282\u70B9\u6302\u8F7D\uFF08skill / rule\uFF09", hint: "\u7ED9\u6BCF\u4E2A\u9636\u6BB5\u6302\u300C\u8FD9\u4E2A\u9636\u6BB5\u7528\u4EC0\u4E48 skill\u300D\u548C\u300C\u5B88\u54EA\u6761 rule\u300D\u3002\u5927\u6A21\u578B\u8D70\u5230\u8BE5\u9636\u6BB5\u65F6\uFF0Cdev_task \u53EA\u62AB\u9732\u8FD9\u9636\u6BB5\u6302\u8F7D\u7684\u5185\u5BB9\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(BindingEditor, { config, setConfig, skills, rules }) }),
4864
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(SectionCard, { icon: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.IconPlusOutline16, { size: 16 }), title: "\u65B0\u5EFA skill / rule", hint: "\u6E05\u5355\u91CC\u6CA1\u6709\u5408\u9002\u7684\u5C31\u5F53\u573A\u65B0\u5EFA\uFF0C\u5199\u5230\u9879\u76EE .dsh/ \u6216\u7528\u6237 $DSH_HOME\uFF1B\u5199\u597D\u540E\u81EA\u52A8\u51FA\u73B0\u5728\u4E0A\u9762\u7684\u6302\u8F7D\u6E05\u5355\u3002", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(ResourceCreator, { remote, workspace, onCreated: refreshCatalogs }) }),
4555
4865
  problems.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.problems, children: [
4556
4866
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("span", { style: styles.problemsTitle, children: [
4557
4867
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.StateDot, { state: "error" }),
@@ -4602,7 +4912,7 @@ function sourceText(source) {
4602
4912
  return "\u5185\u7F6E\u9ED8\u8BA4\u914D\u7F6E\uFF08\u5DE5\u4F5C\u533A\u5C1A\u65E0 .dsh/eng.json\uFF09";
4603
4913
  }
4604
4914
  }
4605
- function SectionCard({ icon, title, children }) {
4915
+ function SectionCard({ icon, title, hint, children }) {
4606
4916
  const [open, setOpen] = (0, import_react2.useState)(true);
4607
4917
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.section, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
4608
4918
  import_dsh_client_ui_primitives.DisclosureRow,
@@ -4615,10 +4925,30 @@ function SectionCard({ icon, title, children }) {
4615
4925
  setOpen(!open);
4616
4926
  },
4617
4927
  keepContentWhenOpen: true,
4618
- children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.cardBody, children })
4928
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.cardBody, children: [
4929
+ hint !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.muted, children: hint }) : null,
4930
+ children
4931
+ ] })
4619
4932
  }
4620
4933
  ) });
4621
4934
  }
4935
+ function TemplatePicker({ config, setConfig }) {
4936
+ const apply2 = (id) => {
4937
+ const template = WORKFLOW_TEMPLATES.find((t) => t.id === id);
4938
+ if (template === void 0) return;
4939
+ setConfig(cloneConfig(template.config));
4940
+ };
4941
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.field, children: [
4942
+ /* @__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" }),
4943
+ /* @__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: () => {
4944
+ apply2(t.id);
4945
+ }, title: t.description, children: t.label }, t.id)) }),
4946
+ /* @__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" })
4947
+ ] });
4948
+ }
4949
+ function cloneConfig(config) {
4950
+ return JSON.parse(JSON.stringify(config));
4951
+ }
4622
4952
  function StageEditor({ config, setConfig }) {
4623
4953
  const [draft, setDraft] = (0, import_react2.useState)("");
4624
4954
  const addStage = () => {
@@ -4861,6 +5191,177 @@ function CommitEditor({ config, setConfig }) {
4861
5191
  ] })
4862
5192
  ] });
4863
5193
  }
5194
+ function BindingEditor({ config, setConfig, skills, rules }) {
5195
+ const toggleSkill = (stage, skill) => {
5196
+ update(config, setConfig, (c) => {
5197
+ const bindings = c.stage_bindings ?? {};
5198
+ const current = bindings[stage]?.skills ?? [];
5199
+ const next = current.includes(skill) ? current.filter((s) => s !== skill) : [...current, skill].sort();
5200
+ return { ...c, stage_bindings: { ...bindings, [stage]: { ...bindings[stage] ?? {}, skills: next } } };
5201
+ });
5202
+ };
5203
+ const toggleRule = (stage, rule) => {
5204
+ update(config, setConfig, (c) => {
5205
+ const bindings = c.stage_bindings ?? {};
5206
+ const current = bindings[stage]?.rules ?? [];
5207
+ const next = current.includes(rule) ? current.filter((s) => s !== rule) : [...current, rule].sort();
5208
+ return { ...c, stage_bindings: { ...bindings, [stage]: { ...bindings[stage] ?? {}, rules: next } } };
5209
+ });
5210
+ };
5211
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { style: styles.section, children: config.stages.map((stage) => {
5212
+ const binding = config.stage_bindings?.[stage];
5213
+ const boundSkills = binding?.skills ?? [];
5214
+ const boundRules = binding?.rules ?? [];
5215
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5216
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.bindingLabel, children: stage }),
5217
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.bindingStage, children: [
5218
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "skill\uFF08\u505A\u4EC0\u4E48\uFF09" }),
5219
+ /* @__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\uFF0C\u53EF\u5728\u4E0B\u65B9\u65B0\u5EFA" }) : skills.map((sk) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5220
+ import_dsh_client_ui_primitives.Pill,
5221
+ {
5222
+ active: boundSkills.includes(sk.name),
5223
+ onClick: () => {
5224
+ toggleSkill(stage, sk.name);
5225
+ },
5226
+ title: `${sk.description}\uFF08${sourceLabel(sk.source)}\uFF09`,
5227
+ children: sk.name
5228
+ },
5229
+ sk.name
5230
+ )) })
5231
+ ] }),
5232
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.bindingStage, children: [
5233
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "rule\uFF08\u5B88\u4EC0\u4E48\uFF09" }),
5234
+ /* @__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\uFF0C\u53EF\u5728\u4E0B\u65B9\u65B0\u5EFA" }) : rules.map((rule) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
5235
+ import_dsh_client_ui_primitives.Pill,
5236
+ {
5237
+ active: boundRules.includes(rule.name),
5238
+ onClick: () => {
5239
+ toggleRule(stage, rule.name);
5240
+ },
5241
+ title: `\u6765\u6E90\uFF1A${sourceLabel(rule.source)}`,
5242
+ children: rule.name
5243
+ },
5244
+ rule.name
5245
+ )) })
5246
+ ] })
5247
+ ] }, stage);
5248
+ }) });
5249
+ }
5250
+ function ResourceCreator({ remote, workspace, onCreated }) {
5251
+ const [skillName, setSkillName] = (0, import_react2.useState)("");
5252
+ const [skillDesc, setSkillDesc] = (0, import_react2.useState)("");
5253
+ const [skillWhen, setSkillWhen] = (0, import_react2.useState)("");
5254
+ const [skillBody, setSkillBody] = (0, import_react2.useState)("");
5255
+ const [ruleName, setRuleName] = (0, import_react2.useState)("");
5256
+ const [ruleBody, setRuleBody] = (0, import_react2.useState)("");
5257
+ const [level, setLevel] = (0, import_react2.useState)("project");
5258
+ const [msg, setMsg] = (0, import_react2.useState)("");
5259
+ const createSkill = async () => {
5260
+ setMsg("");
5261
+ const result = await remote.writeSkill({
5262
+ name: skillName,
5263
+ description: skillDesc,
5264
+ whenToUse: skillWhen === "" ? void 0 : skillWhen,
5265
+ content: skillBody,
5266
+ level,
5267
+ path: workspace
5268
+ });
5269
+ if (!result.ok) {
5270
+ setMsg("\u65B0\u5EFA skill \u5931\u8D25\uFF1A" + describeError(result.error));
5271
+ return;
5272
+ }
5273
+ if (!result.value.ok) {
5274
+ setMsg("\u65B0\u5EFA skill \u5931\u8D25\uFF1A" + (result.value.error ?? "\u672A\u77E5\u9519\u8BEF"));
5275
+ return;
5276
+ }
5277
+ setMsg(`\u5DF2\u65B0\u5EFA skill\u300C${result.value.name}\u300D`);
5278
+ setSkillName("");
5279
+ setSkillDesc("");
5280
+ setSkillWhen("");
5281
+ setSkillBody("");
5282
+ onCreated();
5283
+ };
5284
+ const createRule = async () => {
5285
+ setMsg("");
5286
+ const result = await remote.writeRule({ name: ruleName, content: ruleBody, level, path: workspace });
5287
+ if (!result.ok) {
5288
+ setMsg("\u65B0\u5EFA rule \u5931\u8D25\uFF1A" + describeError(result.error));
5289
+ return;
5290
+ }
5291
+ if (!result.value.ok) {
5292
+ setMsg("\u65B0\u5EFA rule \u5931\u8D25\uFF1A" + (result.value.error ?? "\u672A\u77E5\u9519\u8BEF"));
5293
+ return;
5294
+ }
5295
+ setMsg(`\u5DF2\u65B0\u5EFA rule\u300C${result.value.name}\u300D`);
5296
+ setRuleName("");
5297
+ setRuleBody("");
5298
+ onCreated();
5299
+ };
5300
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.section, children: [
5301
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5302
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.fieldLabel, children: "\u5199\u5165\u4F4D\u7F6E" }),
5303
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.chips, children: [
5304
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: level === "project", onClick: () => {
5305
+ setLevel("project");
5306
+ }, title: "\u5199\u5230\u5DE5\u4F5C\u533A .dsh/\uFF0C\u56E2\u961F\u5171\u4EAB", children: "\u9879\u76EE\u7EA7" }),
5307
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_dsh_client_ui_primitives.Pill, { active: level === "user", onClick: () => {
5308
+ setLevel("user");
5309
+ }, title: "\u5199\u5230 $DSH_HOME\uFF0C\u4E2A\u4EBA\u6240\u6709\u9879\u76EE\u53EF\u7528", children: "\u7528\u6237\u7EA7" })
5310
+ ] })
5311
+ ] }),
5312
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5313
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.bindingLabel, children: "\u65B0\u5EFA Skill" }),
5314
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: styles.row, children: [
5315
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 140px" }, placeholder: "name\uFF08\u5982 my-skill\uFF09", value: skillName, onChange: (ev) => {
5316
+ setSkillName(ev.target.value);
5317
+ } }),
5318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: { ...control, flex: "1 1 240px" }, placeholder: "description\uFF08\u4E00\u53E5\u8BDD\u8BF4\u660E\u5B83\u505A\u4EC0\u4E48\uFF09", value: skillDesc, onChange: (ev) => {
5319
+ setSkillDesc(ev.target.value);
5320
+ } })
5321
+ ] }),
5322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, placeholder: "whenToUse\uFF08\u53EF\u9009\uFF1A\u4EC0\u4E48\u60C5\u51B5\u8BE5\u7528\uFF09", value: skillWhen, onChange: (ev) => {
5323
+ setSkillWhen(ev.target.value);
5324
+ } }),
5325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { style: styles.textarea, placeholder: "skill \u6B63\u6587\uFF08\u544A\u8BC9\u5927\u6A21\u578B\u8FD9\u4E2A\u9636\u6BB5\u600E\u4E48\u5E72\uFF09", value: skillBody, onChange: (ev) => {
5326
+ setSkillBody(ev.target.value);
5327
+ } }),
5328
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__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: skillName.trim() === "" || skillDesc.trim() === "" || skillBody.trim() === "", onClick: () => {
5329
+ void createSkill();
5330
+ }, children: "\u65B0\u5EFA skill" }) })
5331
+ ] }),
5332
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: cardV, children: [
5333
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: styles.bindingLabel, children: "\u65B0\u5EFA Rule" }),
5334
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { style: control, placeholder: "name\uFF08\u5982 my-redlines\uFF09", value: ruleName, onChange: (ev) => {
5335
+ setRuleName(ev.target.value);
5336
+ } }),
5337
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("textarea", { style: styles.textarea, placeholder: "rule \u6B63\u6587\uFF08\u8FD9\u6761\u89C4\u5219\u7EA6\u675F\u5927\u6A21\u578B\u9075\u5B88\u4EC0\u4E48\uFF09", value: ruleBody, onChange: (ev) => {
5338
+ setRuleBody(ev.target.value);
5339
+ } }),
5340
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { children: /* @__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: ruleName.trim() === "" || ruleBody.trim() === "", onClick: () => {
5341
+ void createRule();
5342
+ }, children: "\u65B0\u5EFA rule" }) })
5343
+ ] }),
5344
+ msg !== "" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", { style: styles.status, children: msg }) : null
5345
+ ] });
5346
+ }
5347
+ function sourceLabel(source) {
5348
+ switch (source) {
5349
+ case "bundled":
5350
+ return "\u5185\u7F6E";
5351
+ case "project":
5352
+ case "project-dsh":
5353
+ case "project-agents":
5354
+ return "\u9879\u76EE";
5355
+ case "user":
5356
+ case "user-dsh":
5357
+ case "user-agents":
5358
+ return "\u7528\u6237";
5359
+ case "custom":
5360
+ return "\u81EA\u5B9A\u4E49";
5361
+ default:
5362
+ return source;
5363
+ }
5364
+ }
4864
5365
  function update(config, setConfig, fn) {
4865
5366
  setConfig(fn(config));
4866
5367
  }