@suifei/dsh-ai-team 0.3.26 → 0.3.28

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/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.3.28 — 2026-11-10
4
+
5
+ - 收件箱决策项增加逐条「采纳」与「全部采纳建议」;建议直接填入可编辑输入框,不再手抄;
6
+ - 提交仍要求所有问题有答案,采纳后可继续修改。
7
+
8
+ ## 0.3.27 — 2026-11-10
9
+
10
+ - 主对话「任务」模式拆成标题 + 验收标准;不再把 Human 原话直接当 resultDefinition;
11
+ - 开启 QA 时必须逐行填写 checklist,否则不能提交;
12
+ - 修复真实项目 #3/#4 的根因:重复创建、宽泛结果定义、QA 空清单;
13
+ - 重复 Task 的第二条会打开已有卡,不再静默新建。
14
+
3
15
  ## 0.3.26 — 2026-11-10
4
16
 
5
17
  - Task 创建增加重复保护:相同 title/resultDefinition/owner 的 active Task 会被拒绝并返回已有 Task;
package/client.js CHANGED
@@ -34,7 +34,12 @@ window.__ModuleLoader__.load({
34
34
  tabSituation: '态势',
35
35
  tabsAria: '项目视图',
36
36
  composer: '写一条消息…',
37
- taskPlaceholder: '一句话任务,可稍后补完判据',
37
+ taskPlaceholder: '任务标题:要做什么',
38
+ taskDefinitionPlaceholder: '验收标准(必填):怎样算完成、拿什么证据',
39
+ taskDefinitionAria: 'Task 验收标准',
40
+ qaChecklistPlaceholder: 'QA 清单,每行一条(开启 QA 时必填)',
41
+ qaChecklistAria: 'QA 验收清单',
42
+ taskDefinitionRequired: '请先写验收标准,再创建 Task。',
38
43
  discuss: '讨论',
39
44
  taskMode: '任务',
40
45
  send: '发送',
@@ -110,6 +115,9 @@ window.__ModuleLoader__.load({
110
115
  decisionChoose: '请选择…',
111
116
  decisionAnswer: '填写你的决定',
112
117
  decisionRecommend: '推荐',
118
+ decisionAdopt: '采纳',
119
+ decisionAdoptAll: '全部采纳建议',
120
+ decisionAdoptHint: '建议可直接采纳,再按需修改',
113
121
  decisionSubmit: '提交决策',
114
122
  decisionSubmitted: '决策已提交给团队。',
115
123
  inboxEmptyAction: '看任务看板',
@@ -341,7 +349,12 @@ window.__ModuleLoader__.load({
341
349
  tabSituation: 'Situation',
342
350
  tabsAria: 'Project views',
343
351
  composer: 'Write a message…',
344
- taskPlaceholder: 'A one-line task; add the definition of done later',
352
+ taskPlaceholder: 'Task title: what to do',
353
+ taskDefinitionPlaceholder: 'Definition of done (required): how it is accepted, with what evidence',
354
+ taskDefinitionAria: 'Definition of done',
355
+ qaChecklistPlaceholder: 'QA checklist, one per line (required when QA is on)',
356
+ qaChecklistAria: 'QA checklist',
357
+ taskDefinitionRequired: 'Write the definition of done before creating a Task.',
345
358
  discuss: 'Discuss',
346
359
  taskMode: 'Task',
347
360
  send: 'Send',
@@ -417,6 +430,9 @@ window.__ModuleLoader__.load({
417
430
  decisionChoose: 'Choose…',
418
431
  decisionAnswer: 'Write your decision',
419
432
  decisionRecommend: 'Recommended',
433
+ decisionAdopt: 'Adopt',
434
+ decisionAdoptAll: 'Adopt all suggestions',
435
+ decisionAdoptHint: 'Adopt a suggestion, then edit if needed',
420
436
  decisionSubmit: 'Submit decision',
421
437
  decisionSubmitted: 'Decision sent to the team.',
422
438
  inboxEmptyAction: 'Open the board',
@@ -970,6 +986,10 @@ window.__ModuleLoader__.load({
970
986
  .at-inboxoptions { display: block; color: var(--dsw-alias-label-tertiary); font-size: 11px; }
971
987
  .at-inboxrecommend { display: block; color: var(--dsw-alias-state-business-primary); font-size: 11px; }
972
988
  .at-decision { display: flex; flex-direction: column; gap: 8px; margin: 6px 0 4px; }
989
+ .at-decisionhead { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
990
+ .at-decisionhint { color: var(--dsw-alias-label-tertiary); font-size: 11px; }
991
+ .at-decisionrecommend { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 3px; color: var(--dsw-alias-state-business-primary); font-size: 11px; }
992
+ .at-adopt { flex: 0 0 auto; padding: 0; border: 0; background: transparent; color: var(--dsw-alias-state-business-primary); font: inherit; font-size: 11px; cursor: pointer; text-decoration: underline; }
973
993
  .at-decisionselect, .at-decisioninput { width: 100%; margin-top: 4px; padding: 4px 6px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-sm); background: var(--dsw-alias-bg-base); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; line-height: 18px; }
974
994
  .at-decision .at-btn { align-self: flex-start; }
975
995
  .at-inboxtitle { color: var(--dsw-alias-label-primary); font-size: 13px; line-height: 20px; }
@@ -978,6 +998,9 @@ window.__ModuleLoader__.load({
978
998
  .at-inboxactions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
979
999
  .at-qacheck { display: inline-flex; align-items: center; gap: 4px; color: var(--dsw-alias-label-tertiary); font-size: 11px; white-space: nowrap; }
980
1000
  .at-qacheck input { accent-color: var(--dsw-alias-state-business-primary); }
1001
+ .at-taskfields { display: flex; flex-direction: column; gap: 6px; padding: 0 10px 8px; }
1002
+ .at-taskdef, .at-taskqa { width: 100%; box-sizing: border-box; resize: vertical; min-height: 42px; padding: 7px 8px; border: 1px solid var(--dsw-alias-border-l2); border-radius: var(--dsw-radius-sm); background: var(--dsw-alias-bg-layer-2); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; line-height: 18px; }
1003
+ .at-taskdef::placeholder, .at-taskqa::placeholder { color: var(--dsw-alias-label-caption); }
981
1004
  .at-qabadge { display: inline-flex; align-items: center; margin-left: 5px; padding: 0 5px; border: 1px solid var(--dsw-alias-state-business-primary); border-radius: 999px; color: var(--dsw-alias-state-business-primary); font-size: 9px; line-height: 15px; vertical-align: 1px; }
982
1005
  .at-talented { opacity: 0.82; }
983
1006
  .at-btn-small { min-height: 24px; padding: 0 8px; font-size: 11px; }
@@ -1637,6 +1660,9 @@ window.__ModuleLoader__.load({
1637
1660
  const [answers, setAnswers] = React.useState(() => questions.map(() => ''));
1638
1661
  const [busy, setBusy] = React.useState(false);
1639
1662
  const setAnswer = (index, value) => setAnswers((prev) => prev.map((entry, i) => i === index ? value : entry));
1663
+ const recommendations = questions.map((question) => typeof question.recommendation === 'string' ? question.recommendation.trim() : '');
1664
+ const hasRecommendations = recommendations.some((value) => value !== '');
1665
+ const adoptAll = () => setAnswers((prev) => prev.map((entry, index) => recommendations[index] || entry));
1640
1666
  const complete = questions.every((question, index) => answers[index].trim() !== '');
1641
1667
  const submit = async () => {
1642
1668
  if (!complete || busy) return;
@@ -1648,6 +1674,9 @@ window.__ModuleLoader__.load({
1648
1674
  }
1649
1675
  };
1650
1676
  return h('div', { className: 'at-decision' },
1677
+ hasRecommendations ? h('div', { className: 'at-decisionhead' },
1678
+ h('span', { className: 'at-decisionhint' }, t('decisionAdoptHint')),
1679
+ h('button', { className: 'at-btn at-btn-small', type: 'button', onClick: adoptAll }, t('decisionAdoptAll'))) : null,
1651
1680
  h('ol', { className: 'at-inboxquestions' }, questions.map((question, index) => h('li', { key: question.id ?? index },
1652
1681
  h('span', { className: 'at-inboxquestion' }, question.text),
1653
1682
  Array.isArray(question.options) && question.options.length
@@ -1655,7 +1684,9 @@ window.__ModuleLoader__.load({
1655
1684
  h('option', { value: '' }, t('decisionChoose')),
1656
1685
  question.options.map((option) => h('option', { key: option, value: option }, option)))
1657
1686
  : h('input', { className: 'at-decisioninput', value: answers[index], placeholder: question.recommendation ?? t('decisionAnswer'), onChange: (event) => setAnswer(index, event.target.value) }),
1658
- question.recommendation ? h('span', { className: 'at-inboxrecommend' }, t('decisionRecommend') + ':' + question.recommendation) : null))),
1687
+ question.recommendation ? h('div', { className: 'at-decisionrecommend' },
1688
+ h('span', null, t('decisionRecommend') + ':' + question.recommendation),
1689
+ h('button', { className: 'at-adopt', type: 'button', onClick: () => setAnswer(index, question.recommendation) }, t('decisionAdopt'))) : null))),
1659
1690
  h('button', { className: 'at-btn at-btn-primary at-btn-small', type: 'button', disabled: !complete || busy, onClick: submit }, busy ? t('saving') : t('decisionSubmit')));
1660
1691
  }
1661
1692
 
@@ -1919,8 +1950,12 @@ window.__ModuleLoader__.load({
1919
1950
  h('button', { className: 'at-send', type: 'button', disabled: !reply.trim(), onClick: onReplySend }, t('send'))))) : null);
1920
1951
  }
1921
1952
 
1922
- function Composer({ t, mode, draft, disabled, qaRequired, onQaRequired, onMode, onDraft, onSend }) {
1923
- const send = () => { if (draft.trim() && !disabled) onSend(); };
1953
+ function Composer({ t, mode, draft, definition, qaChecklist, disabled, qaRequired, onQaRequired, onDefinition, onQaChecklist, onMode, onDraft, onSend }) {
1954
+ const send = () => {
1955
+ if (!draft.trim() || disabled) return;
1956
+ if (mode === 'task' && (!definition.trim() || (qaRequired && !qaChecklist.trim()))) return;
1957
+ onSend();
1958
+ };
1924
1959
  return h('div', { className: 'at-composer' },
1925
1960
  h('div', { className: 'at-composerinner' },
1926
1961
  h('div', { className: cx('at-box', 'at-composerbox', mode === 'task' && 'at-taskmode') },
@@ -1938,6 +1973,25 @@ window.__ModuleLoader__.load({
1938
1973
  }
1939
1974
  },
1940
1975
  }),
1976
+ mode === 'task' ? h('div', { className: 'at-taskfields' },
1977
+ h('textarea', {
1978
+ className: 'at-taskdef',
1979
+ rows: 2,
1980
+ value: definition,
1981
+ disabled,
1982
+ placeholder: t('taskDefinitionPlaceholder'),
1983
+ 'aria-label': t('taskDefinitionAria'),
1984
+ onChange: (event) => onDefinition(event.target.value),
1985
+ }),
1986
+ qaRequired ? h('textarea', {
1987
+ className: 'at-taskqa',
1988
+ rows: 2,
1989
+ value: qaChecklist,
1990
+ disabled,
1991
+ placeholder: t('qaChecklistPlaceholder'),
1992
+ 'aria-label': t('qaChecklistAria'),
1993
+ onChange: (event) => onQaChecklist(event.target.value),
1994
+ }) : null) : null,
1941
1995
  h('div', { className: 'at-barrow' },
1942
1996
  h('div', { className: 'at-mode', role: 'group', 'aria-label': t('modeAria') },
1943
1997
  h('button', { type: 'button', 'aria-pressed': mode === 'discuss', onClick: () => onMode('discuss') }, t('discuss')),
@@ -1946,7 +2000,7 @@ window.__ModuleLoader__.load({
1946
2000
  h('input', { type: 'checkbox', checked: qaRequired, onChange: (event) => onQaRequired(event.target.checked) }),
1947
2001
  h('span', null, t('qaRequired'))) : null,
1948
2002
  h('span', { className: 'at-hint' }, t('enterHint')),
1949
- h('button', { className: 'at-send', type: 'button', disabled: disabled || !draft.trim(), onClick: send }, mode === 'task' ? t('createTask') : t('send'))))));
2003
+ h('button', { className: 'at-send', type: 'button', disabled: disabled || !draft.trim() || (mode === 'task' && (!definition.trim() || (qaRequired && !qaChecklist.trim()))), onClick: send }, mode === 'task' ? t('createTask') : t('send'))))));
1950
2004
  }
1951
2005
 
1952
2006
  function QuadrantCard({ t, meta, quadrant, tasks, items, count, onItem, onViewAll }) {
@@ -2119,6 +2173,8 @@ window.__ModuleLoader__.load({
2119
2173
  const [threadOpen, setThreadOpen] = React.useState(false);
2120
2174
  const [mode, setMode] = React.useState('discuss');
2121
2175
  const [qaTask, setQaTask] = React.useState(false);
2176
+ const [taskDefinition, setTaskDefinition] = React.useState('');
2177
+ const [qaChecklistText, setQaChecklistText] = React.useState('');
2122
2178
  const [draft, setDraft] = React.useState('');
2123
2179
  const [note, setNote] = React.useState('');
2124
2180
  const [rightNote, setRightNote] = React.useState('');
@@ -2821,7 +2877,18 @@ window.__ModuleLoader__.load({
2821
2877
  if (!project?.workspacePath) { say(t('workspaceRequired')); return; }
2822
2878
  try {
2823
2879
  if (mode === 'task') {
2824
- await runCommand('task.create', { title: text, resultDefinition: text, qaRequired: qaTask });
2880
+ const definition = taskDefinition.trim();
2881
+ const checklist = qaChecklistText.split(/\r?\n/).map((item) => item.trim()).filter(Boolean);
2882
+ if (definition === '') { say(t('taskDefinitionRequired')); return; }
2883
+ if (qaTask && checklist.length === 0) { say(t('qaNeedsChecklist')); return; }
2884
+ await runCommand('task.create', {
2885
+ title: text,
2886
+ resultDefinition: definition,
2887
+ qaRequired: qaTask,
2888
+ ...(qaTask ? { qaChecklist: checklist } : {}),
2889
+ });
2890
+ setTaskDefinition('');
2891
+ setQaChecklistText('');
2825
2892
  } else {
2826
2893
  await runCommand('stream.post', { body: text });
2827
2894
  }
@@ -3171,7 +3238,11 @@ window.__ModuleLoader__.load({
3171
3238
  draft,
3172
3239
  disabled: !project || !project.workspacePath,
3173
3240
  qaRequired: qaTask,
3241
+ definition: taskDefinition,
3242
+ qaChecklist: qaChecklistText,
3174
3243
  onQaRequired: setQaTask,
3244
+ onDefinition: setTaskDefinition,
3245
+ onQaChecklist: setQaChecklistText,
3175
3246
  onMode: setMode,
3176
3247
  onDraft: setDraft,
3177
3248
  onSend: send,
package/index.js CHANGED
@@ -34,7 +34,7 @@ export const name = 'ai-team';
34
34
  /** The workbench only makes sense where the Web carrier exists. */
35
35
  export const inject = ['webServer', 'permissionPresets', 'sandboxPolicy'];
36
36
 
37
- const VERSION = '0.3.26';
37
+ const VERSION = '0.3.28';
38
38
  const PROJECT_ID = 'prj_main';
39
39
  const HUMAN_ID = 'mem_human';
40
40
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@suifei/dsh-ai-team",
3
- "version": "0.3.26",
3
+ "version": "0.3.28",
4
4
  "type": "module",
5
5
  "description": "文渊阁 (Wenyuan Pavilion) — AI Team mode for DeepSeek Harness: concurrent projects, owned tasks, threads, inbox and an append-only team ledger",
6
6
  "license": "MIT",