@xirang-ai/dsh-teamflow 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +15 -11
- package/README.md +15 -11
- package/RELEASE.md +6 -6
- package/cordis.patch.yml +6 -3
- package/lib/client.js +196 -39
- package/lib/index.d.ts +7 -1
- package/lib/index.js +10 -4
- package/lib/remote/contract.d.ts +34 -1
- package/lib/remote/contract.js +16 -0
- package/lib/remote/service.d.ts +15 -2
- package/lib/remote/service.js +49 -6
- package/lib/requirement-discussion.d.ts +23 -0
- package/lib/requirement-discussion.js +26 -0
- package/lib/store/db.d.ts +8 -3
- package/lib/store/db.js +34 -2
- package/lib/store/docs.d.ts +9 -11
- package/lib/store/docs.js +25 -52
- package/lib/store/model.d.ts +32 -5
- package/lib/store/model.js +9 -4
- package/lib/store/paths.d.ts +20 -0
- package/lib/store/paths.js +54 -1
- package/lib/store/schema.js +71 -14
- package/lib/store/workflow.d.ts +50 -25
- package/lib/store/workflow.js +275 -100
- package/lib/tools/delete-design-doc.d.ts +24 -0
- package/lib/tools/delete-design-doc.js +109 -0
- package/lib/tools/get-bug.js +1 -1
- package/lib/tools/get-design-doc.js +1 -1
- package/lib/tools/get-requirement.js +1 -1
- package/lib/tools/get-story.js +1 -1
- package/lib/tools/index.d.ts +1 -1
- package/lib/tools/index.js +12 -1
- package/lib/tools/link-session.d.ts +3 -0
- package/lib/tools/link-session.js +87 -0
- package/locale/en.json +3 -0
- package/locale/zh.json +3 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* @xirang-ai/dsh-teamflow 0.1.
|
|
1
|
+
/* @xirang-ai/dsh-teamflow 0.1.3 — generated by scripts/build.mjs, do not edit. */
|
|
2
2
|
window.__ModuleLoader__.load({
|
|
3
3
|
id: "@xirang-ai/dsh-teamflow",
|
|
4
4
|
factory: (require) => {
|
|
@@ -341,6 +341,7 @@ window.__ModuleLoader__.load({
|
|
|
341
341
|
"delete.statusMissing": "—",
|
|
342
342
|
"delete.summary": "{total} record(s), {deletable} deletable",
|
|
343
343
|
"detail.branch": "Branch",
|
|
344
|
+
"detail.bugDiscussion": "Discuss this bug in a new session",
|
|
344
345
|
"detail.bugBody": "What happened",
|
|
345
346
|
"detail.completedAt": "Completed",
|
|
346
347
|
"detail.description": "Description",
|
|
@@ -356,6 +357,7 @@ window.__ModuleLoader__.load({
|
|
|
356
357
|
"detail.discussionNewDetail": "Discuss the detailed design in a new conversation",
|
|
357
358
|
"detail.discussionNewSplit": "Split Stories in a new conversation",
|
|
358
359
|
"detail.discussionOpen": "Open",
|
|
360
|
+
"detail.storyDiscussion": "Discuss this story in a new session",
|
|
359
361
|
"detail.discussionStarting": "Creating conversation…",
|
|
360
362
|
"detail.docs": "Detailed design",
|
|
361
363
|
"detail.docsEmpty": "No detailed design written yet.",
|
|
@@ -424,6 +426,7 @@ window.__ModuleLoader__.load({
|
|
|
424
426
|
"page.retry": "Retry",
|
|
425
427
|
"page.subtitle": "Defects come first, then running work, work waiting to start, and requirements still in discussion.",
|
|
426
428
|
"page.title": "Xirang Workbench",
|
|
429
|
+
"page.version": "v{version}",
|
|
427
430
|
panel: "Xirang Workbench",
|
|
428
431
|
"remote.bugId.empty": "A bug id is required.",
|
|
429
432
|
"remote.doc.notFound": "Detailed-design document {id} does not exist.",
|
|
@@ -679,6 +682,7 @@ window.__ModuleLoader__.load({
|
|
|
679
682
|
"delete.statusMissing": "—",
|
|
680
683
|
"delete.summary": "共 {total} 条,可删除 {deletable} 条",
|
|
681
684
|
"detail.branch": "分支",
|
|
685
|
+
"detail.bugDiscussion": "在新会话中进行缺陷讨论",
|
|
682
686
|
"detail.bugBody": "缺陷说明",
|
|
683
687
|
"detail.completedAt": "完成时间",
|
|
684
688
|
"detail.description": "故事说明",
|
|
@@ -694,6 +698,7 @@ window.__ModuleLoader__.load({
|
|
|
694
698
|
"detail.discussionNewDetail": "在新会话中进行详细设计讨论",
|
|
695
699
|
"detail.discussionNewSplit": "在新会话中进行故事拆分",
|
|
696
700
|
"detail.discussionOpen": "打开",
|
|
701
|
+
"detail.storyDiscussion": "在新会话中进行故事讨论",
|
|
697
702
|
"detail.discussionStarting": "正在创建会话…",
|
|
698
703
|
"detail.docs": "详细设计",
|
|
699
704
|
"detail.docsEmpty": "还没有写详细设计。",
|
|
@@ -762,6 +767,7 @@ window.__ModuleLoader__.load({
|
|
|
762
767
|
"page.retry": "重试",
|
|
763
768
|
"page.subtitle": "缺陷优先;其下依次是执行中的需求与故事、待开始的工作,以及仍在讨论的需求。",
|
|
764
769
|
"page.title": "息壤工作台",
|
|
770
|
+
"page.version": "v{version}",
|
|
765
771
|
panel: "息壤工作台",
|
|
766
772
|
"remote.bugId.empty": "缺少缺陷编号。",
|
|
767
773
|
"remote.doc.notFound": "详细设计 {id} 不存在。",
|
|
@@ -897,6 +903,7 @@ window.__ModuleLoader__.load({
|
|
|
897
903
|
cancelStory: "cancelStory",
|
|
898
904
|
completeBug: "completeBug",
|
|
899
905
|
prepareRequirementSession: "prepareRequirementSession",
|
|
906
|
+
prepareDiscussionSession: "prepareDiscussionSession",
|
|
900
907
|
sessionPermissionTier: "sessionPermissionTier",
|
|
901
908
|
previewDelete: "previewDelete",
|
|
902
909
|
deleteRecords: "deleteRecords",
|
|
@@ -1034,6 +1041,21 @@ window.__ModuleLoader__.load({
|
|
|
1034
1041
|
]),
|
|
1035
1042
|
result: JSON_CODEC
|
|
1036
1043
|
}),
|
|
1044
|
+
Object.freeze({
|
|
1045
|
+
id: `${PACKAGE_NAME}#${METHODS.prepareDiscussionSession}`,
|
|
1046
|
+
service: SERVICE_KEY,
|
|
1047
|
+
namespace: NAMESPACE,
|
|
1048
|
+
method: METHODS.prepareDiscussionSession,
|
|
1049
|
+
invocation: { kind: "direct" },
|
|
1050
|
+
parameters: Object.freeze([
|
|
1051
|
+
parameter("subjectType", "Discussion subject: requirement, story or bug"),
|
|
1052
|
+
parameter("subjectId", "Requirement, story or defect the discussion belongs to"),
|
|
1053
|
+
parameter("sessionId", "Newly created conversation to link"),
|
|
1054
|
+
parameter("kind", "Discussion kind: discuss for stories and defects, the requirement kinds otherwise"),
|
|
1055
|
+
LOCALE_PARAMETER
|
|
1056
|
+
]),
|
|
1057
|
+
result: JSON_CODEC
|
|
1058
|
+
}),
|
|
1037
1059
|
Object.freeze({
|
|
1038
1060
|
id: `${PACKAGE_NAME}#${METHODS.sessionPermissionTier}`,
|
|
1039
1061
|
service: SERVICE_KEY,
|
|
@@ -1151,6 +1173,15 @@ window.__ModuleLoader__.load({
|
|
|
1151
1173
|
}
|
|
1152
1174
|
return ["【继续讨论需求与概要设计】", "", "我的补充:"].join("\n");
|
|
1153
1175
|
}
|
|
1176
|
+
var STORY_DISCUSSION_PRESET = "xirang-story";
|
|
1177
|
+
var BUG_DISCUSSION_PRESET = "xirang-bug";
|
|
1178
|
+
function presetForRecordDiscussion(kind) {
|
|
1179
|
+
return kind === "bug" ? BUG_DISCUSSION_PRESET : STORY_DISCUSSION_PRESET;
|
|
1180
|
+
}
|
|
1181
|
+
function buildRecordDiscussionPrompt(kind, title) {
|
|
1182
|
+
const heading = kind === "bug" ? "【继续讨论缺陷】" : "【继续讨论故事】";
|
|
1183
|
+
return [heading, title, "", "我的补充:"].join("\n");
|
|
1184
|
+
}
|
|
1154
1185
|
|
|
1155
1186
|
// src/client/composer-kind.ts
|
|
1156
1187
|
var COMPOSER_KINDS = [...CREATE_KINDS, "design", "detail", "split"];
|
|
@@ -1213,6 +1244,14 @@ window.__ModuleLoader__.load({
|
|
|
1213
1244
|
.tf-head { display: flex; align-items: flex-start; gap: 16px; padding: 20px 0 14px; }
|
|
1214
1245
|
.tf-head-main { min-width: 0; flex: 1 1 auto; }
|
|
1215
1246
|
.tf-title { margin: 0; font-size: 18px; font-weight: 650; letter-spacing: -0.01em; }
|
|
1247
|
+
.tf-version {
|
|
1248
|
+
margin-left: 7px;
|
|
1249
|
+
font-size: 11px;
|
|
1250
|
+
font-weight: 500;
|
|
1251
|
+
color: var(--dsw-alias-label-tertiary, inherit);
|
|
1252
|
+
font-variant-numeric: tabular-nums;
|
|
1253
|
+
white-space: nowrap;
|
|
1254
|
+
}
|
|
1216
1255
|
.tf-sub { margin: 3px 0 0; font-size: 12px; color: var(--dsw-alias-label-tertiary, inherit); }
|
|
1217
1256
|
.tf-head-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; padding-top: 2px; }
|
|
1218
1257
|
.tf-error {
|
|
@@ -2886,6 +2925,13 @@ window.__ModuleLoader__.load({
|
|
|
2886
2925
|
const pad = (value2) => String(value2).padStart(2, "0");
|
|
2887
2926
|
return `${pad(at.getMonth() + 1)}-${pad(at.getDate())} ${pad(at.getHours())}:${pad(at.getMinutes())}`;
|
|
2888
2927
|
}
|
|
2928
|
+
function discussionNodes(sessions) {
|
|
2929
|
+
return sessions.map((session) => ({
|
|
2930
|
+
sessionId: session.sessionId,
|
|
2931
|
+
kind: session.kind,
|
|
2932
|
+
at: session.linkedAt ?? session.preparedAt ?? session.createdAt
|
|
2933
|
+
}));
|
|
2934
|
+
}
|
|
2889
2935
|
var EMPTY_BOARD = { requirements: [], standalone: [], bugs: [] };
|
|
2890
2936
|
var STORY_RUNNING = ["developing", "self_testing"];
|
|
2891
2937
|
var STORY_DONE = ["done", "completed", "cancelled"];
|
|
@@ -3016,7 +3062,8 @@ window.__ModuleLoader__.load({
|
|
|
3016
3062
|
acceptance: parsed.acceptance,
|
|
3017
3063
|
bodyMissing: story.bodyMissing,
|
|
3018
3064
|
...last === void 0 ? {} : { lastRun: { status: last.status, at: last.at } },
|
|
3019
|
-
runs
|
|
3065
|
+
runs,
|
|
3066
|
+
sessions: discussionNodes(story.sessions)
|
|
3020
3067
|
};
|
|
3021
3068
|
if (story.requirementId === null) {
|
|
3022
3069
|
standalone.push({ kind: "story", id: story.id, workspaceId: node.workspaceId, story: node });
|
|
@@ -3037,12 +3084,8 @@ window.__ModuleLoader__.load({
|
|
|
3037
3084
|
const finished = stories.length > 0 && stories.every((story) => story.status === "completed");
|
|
3038
3085
|
const state = requirement.status === "archived" ? "done" : running ? "running" : requirement.status === "completed" || finished ? "done" : "ready";
|
|
3039
3086
|
const branch = runs.find((run) => run.branch.length > 0)?.branch;
|
|
3040
|
-
const sessions =
|
|
3041
|
-
(session) =>
|
|
3042
|
-
sessionId: session.sessionId,
|
|
3043
|
-
kind: session.kind,
|
|
3044
|
-
at: session.linkedAt ?? session.preparedAt ?? session.createdAt
|
|
3045
|
-
})
|
|
3087
|
+
const sessions = discussionNodes(
|
|
3088
|
+
snapshot.requirementSessions.filter((session) => session.requirementId === requirement.id)
|
|
3046
3089
|
);
|
|
3047
3090
|
const latestDesign = sessions.find((session) => session.kind === "design");
|
|
3048
3091
|
return {
|
|
@@ -3081,7 +3124,8 @@ window.__ModuleLoader__.load({
|
|
|
3081
3124
|
updatedAt: bug.updatedAt,
|
|
3082
3125
|
...last?.sessionId === void 0 ? {} : { sessionId: last.sessionId },
|
|
3083
3126
|
...bug.createSessionId === null ? {} : { createSessionId: bug.createSessionId },
|
|
3084
|
-
runs
|
|
3127
|
+
runs,
|
|
3128
|
+
sessions: discussionNodes(bug.sessions)
|
|
3085
3129
|
};
|
|
3086
3130
|
});
|
|
3087
3131
|
return { requirements, standalone, bugs };
|
|
@@ -3206,6 +3250,9 @@ window.__ModuleLoader__.load({
|
|
|
3206
3250
|
return title !== void 0 && title.trim() !== "" ? title : sessionId;
|
|
3207
3251
|
}
|
|
3208
3252
|
|
|
3253
|
+
// src/client/version.ts
|
|
3254
|
+
var PLUGIN_VERSION = "0.1.3";
|
|
3255
|
+
|
|
3209
3256
|
// src/dev.ts
|
|
3210
3257
|
var DEV_PRESET = "xirang-dev";
|
|
3211
3258
|
function buildDevPrompt(story, requirement) {
|
|
@@ -14108,6 +14155,7 @@ window.__ModuleLoader__.load({
|
|
|
14108
14155
|
const [createBusy, setCreateBusy] = (0, import_react10.useState)(false);
|
|
14109
14156
|
const [createError, setCreateError] = (0, import_react10.useState)(null);
|
|
14110
14157
|
const [discussionBusy, setDiscussionBusy] = (0, import_react10.useState)(null);
|
|
14158
|
+
const [recordDiscussionBusy, setRecordDiscussionBusy] = (0, import_react10.useState)(null);
|
|
14111
14159
|
const [cleanup, setCleanup] = (0, import_react10.useState)(false);
|
|
14112
14160
|
const [selected2, setSelected] = (0, import_react10.useState)(/* @__PURE__ */ new Set());
|
|
14113
14161
|
const [confirm, setConfirm] = (0, import_react10.useState)(null);
|
|
@@ -14611,14 +14659,59 @@ window.__ModuleLoader__.load({
|
|
|
14611
14659
|
},
|
|
14612
14660
|
[data, openConversation2, showToast, t]
|
|
14613
14661
|
);
|
|
14662
|
+
const startRecordDiscussion = (0, import_react10.useCallback)(
|
|
14663
|
+
(subject) => {
|
|
14664
|
+
const open = openConversation2;
|
|
14665
|
+
if (open === void 0) {
|
|
14666
|
+
showToast(t("toast.startUnavailable"));
|
|
14667
|
+
return;
|
|
14668
|
+
}
|
|
14669
|
+
setRecordDiscussionBusy(subject.kind);
|
|
14670
|
+
open(subject.workspaceId, buildRecordDiscussionPrompt(subject.kind, subject.title), {
|
|
14671
|
+
agentPreset: presetForRecordDiscussion(subject.kind),
|
|
14672
|
+
autoSend: false,
|
|
14673
|
+
composerKind: subject.kind
|
|
14674
|
+
}).then(async (outcome) => {
|
|
14675
|
+
if (outcome.status === "unavailable") {
|
|
14676
|
+
if (mounted.current) {
|
|
14677
|
+
showToast(outcome.message ?? t("toast.discussionFailed", { title: subject.title }));
|
|
14678
|
+
}
|
|
14679
|
+
return;
|
|
14680
|
+
}
|
|
14681
|
+
if (outcome.sessionId === void 0) {
|
|
14682
|
+
if (mounted.current) {
|
|
14683
|
+
showToast(t("toast.discussionDrafted", { title: subject.title }));
|
|
14684
|
+
}
|
|
14685
|
+
return;
|
|
14686
|
+
}
|
|
14687
|
+
try {
|
|
14688
|
+
await data.prepareDiscussionSession(subject.kind, subject.id, outcome.sessionId, "discuss");
|
|
14689
|
+
if (!mounted.current) return;
|
|
14690
|
+
showToast(
|
|
14691
|
+
outcome.status === "drafted" ? t("toast.discussionDrafted", { title: subject.title }) : t("toast.discussionStarted", { title: subject.title })
|
|
14692
|
+
);
|
|
14693
|
+
} catch (failure) {
|
|
14694
|
+
if (!mounted.current) return;
|
|
14695
|
+
showToast(
|
|
14696
|
+
t("toast.discussionLinkFailed", {
|
|
14697
|
+
title: subject.title,
|
|
14698
|
+
message: failureMessage(failure)
|
|
14699
|
+
})
|
|
14700
|
+
);
|
|
14701
|
+
}
|
|
14702
|
+
}).catch(() => {
|
|
14703
|
+
if (mounted.current) showToast(t("toast.discussionFailed", { title: subject.title }));
|
|
14704
|
+
}).finally(() => {
|
|
14705
|
+
if (mounted.current) setRecordDiscussionBusy(null);
|
|
14706
|
+
});
|
|
14707
|
+
},
|
|
14708
|
+
[data, openConversation2, showToast, t]
|
|
14709
|
+
);
|
|
14614
14710
|
const requirementAction = (0, import_react10.useCallback)(
|
|
14615
14711
|
(requirement) => {
|
|
14616
14712
|
if (requirement.state === "running") {
|
|
14617
14713
|
return { label: t("row.open"), tone: "live", icon: ICONS.live };
|
|
14618
14714
|
}
|
|
14619
|
-
if (requirement.state === "done") {
|
|
14620
|
-
return { label: t("row.history"), tone: "ghost", icon: ICONS.ghost };
|
|
14621
|
-
}
|
|
14622
14715
|
const lone = loneReadyStory(requirement);
|
|
14623
14716
|
return lone === void 0 ? null : { label: t("row.start"), tone: "soft", icon: ICONS.soft };
|
|
14624
14717
|
},
|
|
@@ -14713,11 +14806,7 @@ window.__ModuleLoader__.load({
|
|
|
14713
14806
|
}
|
|
14714
14807
|
};
|
|
14715
14808
|
const storyAction = (0, import_react10.useCallback)(
|
|
14716
|
-
(story) => {
|
|
14717
|
-
if (story.state === "running") return { label: t("row.open"), tone: "live", icon: ICONS.live };
|
|
14718
|
-
if (story.state === "done") return { label: t("row.history"), tone: "ghost", icon: ICONS.ghost };
|
|
14719
|
-
return { label: t("row.start"), tone: "soft", icon: ICONS.soft };
|
|
14720
|
-
},
|
|
14809
|
+
(story) => story.state === "ready" ? { label: t("row.start"), tone: "soft", icon: ICONS.soft } : null,
|
|
14721
14810
|
[t]
|
|
14722
14811
|
);
|
|
14723
14812
|
const runStoryAction = (0, import_react10.useCallback)(
|
|
@@ -14998,7 +15087,10 @@ window.__ModuleLoader__.load({
|
|
|
14998
15087
|
view.kind === "list" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
14999
15088
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("header", { className: "tf-head", children: [
|
|
15000
15089
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tf-head-main", children: [
|
|
15001
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.
|
|
15090
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("h1", { className: "tf-title", children: [
|
|
15091
|
+
t("page.title"),
|
|
15092
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("span", { className: "tf-version", children: t("page.version", { version: PLUGIN_VERSION }) })
|
|
15093
|
+
] }),
|
|
15002
15094
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "tf-sub", children: t("page.subtitle") })
|
|
15003
15095
|
] }),
|
|
15004
15096
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tf-head-actions", children: [
|
|
@@ -15243,7 +15335,14 @@ window.__ModuleLoader__.load({
|
|
|
15243
15335
|
onDelete: () => openDelete([{ kind: "bug", id: activeBug.id }]),
|
|
15244
15336
|
onComplete: () => setDecision({ kind: "complete-bug", bug: activeBug, busy: false, error: null }),
|
|
15245
15337
|
onStartFix: (tier) => startBugFix(activeBug, tier),
|
|
15246
|
-
onCancelFix: () => cancelBugFix(activeBug)
|
|
15338
|
+
onCancelFix: () => cancelBugFix(activeBug),
|
|
15339
|
+
onNewDiscussion: () => startRecordDiscussion({
|
|
15340
|
+
kind: "bug",
|
|
15341
|
+
id: activeBug.id,
|
|
15342
|
+
title: activeBug.title,
|
|
15343
|
+
workspaceId: activeBug.workspaceId
|
|
15344
|
+
}),
|
|
15345
|
+
discussionBusy: recordDiscussionBusy === "bug"
|
|
15247
15346
|
}
|
|
15248
15347
|
) : null,
|
|
15249
15348
|
view.kind === "story" && activeStory !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -15263,7 +15362,14 @@ window.__ModuleLoader__.load({
|
|
|
15263
15362
|
onCancelRun: () => setDecision({ kind: "cancel-run", story: activeStory, busy: false, error: null }),
|
|
15264
15363
|
onOpenSession: (sessionId) => openSession(sessionId),
|
|
15265
15364
|
onOpenPath: openFile,
|
|
15266
|
-
onMissing: (path) => showToast(t("doc.linkMissing", { path }))
|
|
15365
|
+
onMissing: (path) => showToast(t("doc.linkMissing", { path })),
|
|
15366
|
+
onNewDiscussion: () => startRecordDiscussion({
|
|
15367
|
+
kind: "story",
|
|
15368
|
+
id: activeStory.id,
|
|
15369
|
+
title: activeStory.title,
|
|
15370
|
+
workspaceId: activeStory.workspaceId
|
|
15371
|
+
}),
|
|
15372
|
+
discussionBusy: recordDiscussionBusy === "story"
|
|
15267
15373
|
}
|
|
15268
15374
|
) : null
|
|
15269
15375
|
] }) }),
|
|
@@ -16140,6 +16246,8 @@ window.__ModuleLoader__.load({
|
|
|
16140
16246
|
onComplete,
|
|
16141
16247
|
onStartFix,
|
|
16142
16248
|
onCancelFix,
|
|
16249
|
+
onNewDiscussion,
|
|
16250
|
+
discussionBusy = false,
|
|
16143
16251
|
readOnly = false
|
|
16144
16252
|
}) {
|
|
16145
16253
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
@@ -16166,27 +16274,42 @@ window.__ModuleLoader__.load({
|
|
|
16166
16274
|
readOnly ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("button", { className: "tf-btn tf-danger", "data-tone": "ghost", type: "button", onClick: onDelete, style: { flex: "0 0 auto" }, children: [
|
|
16167
16275
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(TrashGlyph, {}),
|
|
16168
16276
|
t("delete.action")
|
|
16169
|
-
] })
|
|
16170
|
-
readOnly || bug.sessionId === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
16171
|
-
"button",
|
|
16172
|
-
{
|
|
16173
|
-
className: "tf-btn",
|
|
16174
|
-
"data-tone": "live",
|
|
16175
|
-
type: "button",
|
|
16176
|
-
onClick: () => onOpenSession(bug.sessionId),
|
|
16177
|
-
style: { flex: "0 0 auto" },
|
|
16178
|
-
children: [
|
|
16179
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(ChatGlyph, {}),
|
|
16180
|
-
t("row.open")
|
|
16181
|
-
]
|
|
16182
|
-
}
|
|
16183
|
-
)
|
|
16277
|
+
] })
|
|
16184
16278
|
] }),
|
|
16185
16279
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tf-grid", children: [
|
|
16186
16280
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
16187
16281
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "tf-card", children: [
|
|
16188
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16189
|
-
|
|
16282
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16283
|
+
SectionHead,
|
|
16284
|
+
{
|
|
16285
|
+
title: t("detail.bugBody"),
|
|
16286
|
+
action: readOnly || onNewDiscussion === void 0 ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
16287
|
+
"button",
|
|
16288
|
+
{
|
|
16289
|
+
className: "tf-btn tf-btn-sm",
|
|
16290
|
+
"data-tone": "soft",
|
|
16291
|
+
type: "button",
|
|
16292
|
+
disabled: discussionBusy,
|
|
16293
|
+
onClick: onNewDiscussion,
|
|
16294
|
+
children: [
|
|
16295
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PlusGlyph, {}),
|
|
16296
|
+
discussionBusy ? t("detail.discussionStarting") : t("detail.bugDiscussion")
|
|
16297
|
+
]
|
|
16298
|
+
}
|
|
16299
|
+
)
|
|
16300
|
+
}
|
|
16301
|
+
),
|
|
16302
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "tf-doc-body", children: bug.bodyMissing ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "tf-card-hint", children: t("body.missingGeneric") }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(MarkdownBody, { text: bug.content.join("\n"), t }) }),
|
|
16303
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16304
|
+
DiscussionSessions,
|
|
16305
|
+
{
|
|
16306
|
+
t,
|
|
16307
|
+
sessions: bug.sessions,
|
|
16308
|
+
sessionTitle,
|
|
16309
|
+
onOpenSession,
|
|
16310
|
+
readOnly
|
|
16311
|
+
}
|
|
16312
|
+
)
|
|
16190
16313
|
] }),
|
|
16191
16314
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "tf-card", children: [
|
|
16192
16315
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -16262,6 +16385,8 @@ window.__ModuleLoader__.load({
|
|
|
16262
16385
|
onOpenSession,
|
|
16263
16386
|
onOpenPath,
|
|
16264
16387
|
onMissing,
|
|
16388
|
+
onNewDiscussion,
|
|
16389
|
+
discussionBusy = false,
|
|
16265
16390
|
readOnly = false
|
|
16266
16391
|
}) {
|
|
16267
16392
|
return /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(import_jsx_runtime10.Fragment, { children: [
|
|
@@ -16311,7 +16436,26 @@ window.__ModuleLoader__.load({
|
|
|
16311
16436
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { className: "tf-grid", children: [
|
|
16312
16437
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("div", { children: [
|
|
16313
16438
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "tf-card", children: [
|
|
16314
|
-
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16439
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16440
|
+
SectionHead,
|
|
16441
|
+
{
|
|
16442
|
+
title: t("detail.storyBody"),
|
|
16443
|
+
action: readOnly || onNewDiscussion === void 0 ? void 0 : /* @__PURE__ */ (0, import_jsx_runtime10.jsxs)(
|
|
16444
|
+
"button",
|
|
16445
|
+
{
|
|
16446
|
+
className: "tf-btn tf-btn-sm",
|
|
16447
|
+
"data-tone": "soft",
|
|
16448
|
+
type: "button",
|
|
16449
|
+
disabled: discussionBusy,
|
|
16450
|
+
onClick: onNewDiscussion,
|
|
16451
|
+
children: [
|
|
16452
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(PlusGlyph, {}),
|
|
16453
|
+
discussionBusy ? t("detail.discussionStarting") : t("detail.storyDiscussion")
|
|
16454
|
+
]
|
|
16455
|
+
}
|
|
16456
|
+
)
|
|
16457
|
+
}
|
|
16458
|
+
),
|
|
16315
16459
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)("div", { className: "tf-doc-body", children: story.bodyMissing ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "tf-card-hint", style: { margin: 0 }, children: t("body.missing", { path: story.path }) }) : story.body.trim() === "" ? /* @__PURE__ */ (0, import_jsx_runtime10.jsx)("p", { className: "tf-card-hint", style: { margin: 0 }, children: t("detail.noDescription") }) : /* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16316
16460
|
MarkdownBody,
|
|
16317
16461
|
{
|
|
@@ -16321,7 +16465,17 @@ window.__ModuleLoader__.load({
|
|
|
16321
16465
|
onOpenPath,
|
|
16322
16466
|
onMissing
|
|
16323
16467
|
}
|
|
16324
|
-
) })
|
|
16468
|
+
) }),
|
|
16469
|
+
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
16470
|
+
DiscussionSessions,
|
|
16471
|
+
{
|
|
16472
|
+
t,
|
|
16473
|
+
sessions: story.sessions,
|
|
16474
|
+
sessionTitle,
|
|
16475
|
+
onOpenSession,
|
|
16476
|
+
readOnly
|
|
16477
|
+
}
|
|
16478
|
+
)
|
|
16325
16479
|
] }),
|
|
16326
16480
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsxs)("section", { className: "tf-card", children: [
|
|
16327
16481
|
/* @__PURE__ */ (0, import_jsx_runtime10.jsx)(
|
|
@@ -17091,6 +17245,9 @@ window.__ModuleLoader__.load({
|
|
|
17091
17245
|
prepareRequirementSession: async (requirementId, sessionId, kind) => callRemote(
|
|
17092
17246
|
(await ready.promise).prepareRequirementSession(requirementId, sessionId, kind, locale())
|
|
17093
17247
|
),
|
|
17248
|
+
prepareDiscussionSession: async (subjectType, subjectId, sessionId, kind) => callRemote(
|
|
17249
|
+
(await ready.promise).prepareDiscussionSession(subjectType, subjectId, sessionId, kind, locale())
|
|
17250
|
+
),
|
|
17094
17251
|
sessionPermissionTier: async (sessionId) => callRemote((await ready.promise).sessionPermissionTier(sessionId, locale())),
|
|
17095
17252
|
previewDelete: async (targets) => callRemote((await ready.promise).previewDelete(targets, locale())),
|
|
17096
17253
|
deleteRecords: async (targets) => callRemote((await ready.promise).deleteRecords(targets, locale())),
|
package/lib/index.d.ts
CHANGED
|
@@ -16,6 +16,11 @@ export interface Config {
|
|
|
16
16
|
* which is the only location this plugin owns.
|
|
17
17
|
*/
|
|
18
18
|
dataDir: string;
|
|
19
|
+
/**
|
|
20
|
+
* Document root override. Empty uses `<dataDir>/docs`, next to the database;
|
|
21
|
+
* relative paths resolve against the working directory and `~/` is expanded.
|
|
22
|
+
*/
|
|
23
|
+
docsDir: string;
|
|
19
24
|
/** SQLite busy timeout in milliseconds; a second instance waits this long for the write lock. */
|
|
20
25
|
busyTimeoutMs: number;
|
|
21
26
|
}
|
|
@@ -32,7 +37,8 @@ export declare function apply(ctx: Context, config: Config): void;
|
|
|
32
37
|
export type { TeamflowDatabase, StartupReport, Role } from './store/db.ts';
|
|
33
38
|
export type { SchemaComment } from './store/schema.ts';
|
|
34
39
|
export { WorkflowStore } from './store/workflow.ts';
|
|
35
|
-
export {
|
|
40
|
+
export { docPath, ensureDocTree, resolveDocPath } from './store/docs.ts';
|
|
41
|
+
export { DOCS_DIRNAME, defaultDocsDir, resolveDocsDir } from './store/paths.ts';
|
|
36
42
|
export type { DocKind as DocFileKind } from './store/docs.ts';
|
|
37
43
|
export type { WorkflowStoreOptions } from './store/workflow.ts';
|
|
38
44
|
export { WorkflowError } from './store/model.ts';
|
package/lib/index.js
CHANGED
|
@@ -7,6 +7,9 @@ export const Config = Schema.object({
|
|
|
7
7
|
dataDir: Schema.string()
|
|
8
8
|
.default('')
|
|
9
9
|
.description('Workbench data directory. Empty uses $DSH_HOME/xirang-ai/dsh-teamflow.'),
|
|
10
|
+
docsDir: Schema.string()
|
|
11
|
+
.default('')
|
|
12
|
+
.description('Document root for requirement, design, story and bug bodies. Empty uses <dataDir>/docs; relative paths and ~/ are supported.'),
|
|
10
13
|
busyTimeoutMs: Schema.number()
|
|
11
14
|
.min(0)
|
|
12
15
|
.max(60_000)
|
|
@@ -28,11 +31,13 @@ export function apply(ctx, config) {
|
|
|
28
31
|
error: (message) => logger.error(message),
|
|
29
32
|
};
|
|
30
33
|
ctx.effect(() => {
|
|
31
|
-
// Document bodies live
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
+
// Document bodies live below the plugin-owned document root, not the
|
|
35
|
+
// database and not the user's workspace; the panel reads them by
|
|
36
|
+
// `content_path` relative to that one root. The registry stays wired for
|
|
37
|
+
// workspace display only.
|
|
34
38
|
const database = openTeamflowDatabase({
|
|
35
39
|
dataDir: config.dataDir,
|
|
40
|
+
docsDir: config.docsDir,
|
|
36
41
|
busyTimeoutMs: config.busyTimeoutMs,
|
|
37
42
|
// Resolved lazily: the optional registry may register after this effect runs.
|
|
38
43
|
workspaceRoot: (workspaceId) => ctx.get('workspaceRegistry')?.get(workspaceId)?.path,
|
|
@@ -66,6 +71,7 @@ export function apply(ctx, config) {
|
|
|
66
71
|
}, 'teamflow: workbench database');
|
|
67
72
|
}
|
|
68
73
|
export { WorkflowStore } from "./store/workflow.js";
|
|
69
|
-
export {
|
|
74
|
+
export { docPath, ensureDocTree, resolveDocPath } from "./store/docs.js";
|
|
75
|
+
export { DOCS_DIRNAME, defaultDocsDir, resolveDocsDir } from "./store/paths.js";
|
|
70
76
|
export { WorkflowError } from "./store/model.js";
|
|
71
77
|
export { MAX_DELETE_TARGETS } from "./store/workflow.js";
|
package/lib/remote/contract.d.ts
CHANGED
|
@@ -36,7 +36,7 @@ declare module '@deepseek-ai/dsh-typert-protocol' {
|
|
|
36
36
|
register(contribution: HostContribution): TypertDisposer;
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
|
-
import type { BugSeverity, BugStatus, DeletePreview, DeleteReport, DeleteTarget, DocKind, RequirementSessionKind, RequirementStatus, RunKind, RunStatus, StorySize, StoryStatus } from '../store/model.ts';
|
|
39
|
+
import type { BugSeverity, BugStatus, DeletePreview, DiscussionSubjectType, DeleteReport, DeleteTarget, DocKind, RequirementSessionKind, RequirementStatus, RunKind, RunStatus, StorySize, StoryStatus } from '../store/model.ts';
|
|
40
40
|
/** npm package that owns these Remote methods. */
|
|
41
41
|
export declare const PACKAGE_NAME = "@xirang-ai/dsh-teamflow";
|
|
42
42
|
/**
|
|
@@ -89,6 +89,8 @@ export interface SnapshotStory {
|
|
|
89
89
|
readonly completedAt: string | null;
|
|
90
90
|
/** Conversation that created the story; null when it was not recorded. */
|
|
91
91
|
readonly createSessionId: string | null;
|
|
92
|
+
/** Confirmed discussions of this story, newest first. */
|
|
93
|
+
readonly sessions: readonly SnapshotDiscussionSession[];
|
|
92
94
|
}
|
|
93
95
|
/** One 详细设计 document; its body is fetched on demand by {@link readDoc}. */
|
|
94
96
|
export interface SnapshotDoc {
|
|
@@ -121,6 +123,8 @@ export interface SnapshotBug {
|
|
|
121
123
|
readonly completedAt: string | null;
|
|
122
124
|
/** Conversation that created the defect; null when it was not recorded. */
|
|
123
125
|
readonly createSessionId: string | null;
|
|
126
|
+
/** Confirmed discussions of this defect, newest first. */
|
|
127
|
+
readonly sessions: readonly SnapshotDiscussionSession[];
|
|
124
128
|
}
|
|
125
129
|
/** One development or fix run; `sessionId` is the conversation it happened in. */
|
|
126
130
|
export interface SnapshotRun {
|
|
@@ -148,6 +152,17 @@ export interface SnapshotRequirementSession {
|
|
|
148
152
|
readonly linkedAt: string | null;
|
|
149
153
|
readonly createdAt: string;
|
|
150
154
|
}
|
|
155
|
+
/** One discussion conversation attached to a story or a defect. */
|
|
156
|
+
export interface SnapshotDiscussionSession {
|
|
157
|
+
readonly sessionId: string;
|
|
158
|
+
/** `discuss` for stories and defects; requirements keep their own kinds. */
|
|
159
|
+
readonly kind: string;
|
|
160
|
+
/** Browser preparation time; null only for conversations that skipped it. */
|
|
161
|
+
readonly preparedAt: string | null;
|
|
162
|
+
/** Agent confirmation time; null while the discussion is pending. */
|
|
163
|
+
readonly linkedAt: string | null;
|
|
164
|
+
readonly createdAt: string;
|
|
165
|
+
}
|
|
151
166
|
/** Everything the list, the design tree and the defect list need in one frame. */
|
|
152
167
|
export interface WorkbenchSnapshot {
|
|
153
168
|
/** When the Host read the rows; the panel shows it next to the refresh button. */
|
|
@@ -226,6 +241,16 @@ export interface RequirementSessionReply {
|
|
|
226
241
|
readonly preparedAt: string | null;
|
|
227
242
|
readonly linkedAt: string | null;
|
|
228
243
|
}
|
|
244
|
+
/** One discussion row as stored; `linkedAt` is null until the Agent confirms. */
|
|
245
|
+
export interface DiscussionSessionReply {
|
|
246
|
+
readonly subjectType: DiscussionSubjectType;
|
|
247
|
+
readonly subjectId: string;
|
|
248
|
+
readonly sessionId: string;
|
|
249
|
+
readonly kind: string;
|
|
250
|
+
readonly preparedAt: string | null;
|
|
251
|
+
readonly linkedAt: string | null;
|
|
252
|
+
readonly createdAt: string;
|
|
253
|
+
}
|
|
229
254
|
/** Success or failure envelope the browser mounts produce. */
|
|
230
255
|
export type RemoteResult<Value> = {
|
|
231
256
|
readonly ok: true;
|
|
@@ -323,6 +348,13 @@ export interface WorkbenchRemote {
|
|
|
323
348
|
* of the snapshot until `teamflow_link_requirement_session` writes `linkedAt`.
|
|
324
349
|
*/
|
|
325
350
|
prepareRequirementSession(requirementId: string, sessionId: string, kind: RequirementSessionKind, locale?: string): Promise<RemoteResult<RequirementSessionReply>>;
|
|
351
|
+
/**
|
|
352
|
+
* Register a newly created session as a pending discussion of any subject.
|
|
353
|
+
*
|
|
354
|
+
* Same two-phase contract as {@link prepareRequirementSession}, extended to
|
|
355
|
+
* stories and defects; this method never writes `linkedAt`.
|
|
356
|
+
*/
|
|
357
|
+
prepareDiscussionSession(subjectType: DiscussionSubjectType, subjectId: string, sessionId: string, kind: string, locale?: string): Promise<RemoteResult<DiscussionSessionReply>>;
|
|
326
358
|
/**
|
|
327
359
|
* Read the tier a conversation was started with, for the session header.
|
|
328
360
|
*
|
|
@@ -357,6 +389,7 @@ export declare const METHODS: {
|
|
|
357
389
|
readonly cancelStory: "cancelStory";
|
|
358
390
|
readonly completeBug: "completeBug";
|
|
359
391
|
readonly prepareRequirementSession: "prepareRequirementSession";
|
|
392
|
+
readonly prepareDiscussionSession: "prepareDiscussionSession";
|
|
360
393
|
readonly sessionPermissionTier: "sessionPermissionTier";
|
|
361
394
|
readonly previewDelete: "previewDelete";
|
|
362
395
|
readonly deleteRecords: "deleteRecords";
|
package/lib/remote/contract.js
CHANGED
|
@@ -22,6 +22,7 @@ export const METHODS = {
|
|
|
22
22
|
cancelStory: 'cancelStory',
|
|
23
23
|
completeBug: 'completeBug',
|
|
24
24
|
prepareRequirementSession: 'prepareRequirementSession',
|
|
25
|
+
prepareDiscussionSession: 'prepareDiscussionSession',
|
|
25
26
|
sessionPermissionTier: 'sessionPermissionTier',
|
|
26
27
|
previewDelete: 'previewDelete',
|
|
27
28
|
deleteRecords: 'deleteRecords',
|
|
@@ -170,6 +171,21 @@ export const DESCRIPTORS = Object.freeze([
|
|
|
170
171
|
]),
|
|
171
172
|
result: JSON_CODEC,
|
|
172
173
|
}),
|
|
174
|
+
Object.freeze({
|
|
175
|
+
id: `${PACKAGE_NAME}#${METHODS.prepareDiscussionSession}`,
|
|
176
|
+
service: SERVICE_KEY,
|
|
177
|
+
namespace: NAMESPACE,
|
|
178
|
+
method: METHODS.prepareDiscussionSession,
|
|
179
|
+
invocation: { kind: 'direct' },
|
|
180
|
+
parameters: Object.freeze([
|
|
181
|
+
parameter('subjectType', 'Discussion subject: requirement, story or bug'),
|
|
182
|
+
parameter('subjectId', 'Requirement, story or defect the discussion belongs to'),
|
|
183
|
+
parameter('sessionId', 'Newly created conversation to link'),
|
|
184
|
+
parameter('kind', 'Discussion kind: discuss for stories and defects, the requirement kinds otherwise'),
|
|
185
|
+
LOCALE_PARAMETER,
|
|
186
|
+
]),
|
|
187
|
+
result: JSON_CODEC,
|
|
188
|
+
}),
|
|
173
189
|
Object.freeze({
|
|
174
190
|
id: `${PACKAGE_NAME}#${METHODS.sessionPermissionTier}`,
|
|
175
191
|
service: SERVICE_KEY,
|
package/lib/remote/service.d.ts
CHANGED
|
@@ -10,9 +10,9 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import type { Context } from '@deepseek-ai/cordis';
|
|
12
12
|
import { TypertRemoteService } from '@deepseek-ai/dsh-typert-protocol';
|
|
13
|
-
import type { DeletePreview, DeleteReport, DeleteTarget, RequirementSessionKind } from '../store/model.ts';
|
|
13
|
+
import type { DeletePreview, DeleteReport, DeleteTarget, DiscussionSubjectType, RequirementSessionKind } from '../store/model.ts';
|
|
14
14
|
import type { WorkflowStore } from '../store/workflow.ts';
|
|
15
|
-
import type { AttachmentPolicy, BugRunReply, BugStatusReply, CancelRunReply, DocBody, RequirementSessionReply, RequirementStatusReply, SessionTierReply, StartRunReply, StoryStatusReply, WorkbenchSnapshot } from './contract.ts';
|
|
15
|
+
import type { AttachmentPolicy, BugRunReply, BugStatusReply, CancelRunReply, DiscussionSessionReply, DocBody, RequirementSessionReply, RequirementStatusReply, SessionTierReply, StartRunReply, StoryStatusReply, WorkbenchSnapshot } from './contract.ts';
|
|
16
16
|
/**
|
|
17
17
|
* Remote face of the workbench.
|
|
18
18
|
*
|
|
@@ -134,6 +134,19 @@ export declare class WorkbenchService extends TypertRemoteService {
|
|
|
134
134
|
* @returns the stored pending row.
|
|
135
135
|
*/
|
|
136
136
|
prepareRequirementSession(requirementId: string, sessionId: string, kind?: RequirementSessionKind, locale?: string): RequirementSessionReply;
|
|
137
|
+
/**
|
|
138
|
+
* Register a newly created conversation as a pending discussion of any subject.
|
|
139
|
+
*
|
|
140
|
+
* Same two-phase contract as {@link prepareRequirementSession}: the Agent
|
|
141
|
+
* confirms with `teamflow_link_session`, and this method never writes the
|
|
142
|
+
* confirmation time itself.
|
|
143
|
+
* @param subjectType - requirement, story or bug.
|
|
144
|
+
* @param subjectId - record the discussion belongs to.
|
|
145
|
+
* @param sessionId - session created by the browser for this discussion.
|
|
146
|
+
* @param kind - `discuss` for stories and defects; requirement kinds otherwise.
|
|
147
|
+
* @returns the stored pending row.
|
|
148
|
+
*/
|
|
149
|
+
prepareDiscussionSession(subjectType: DiscussionSubjectType, subjectId: string, sessionId: string, kind: string, locale?: string): DiscussionSessionReply;
|
|
137
150
|
/**
|
|
138
151
|
* Compute deletion impact without writing anything.
|
|
139
152
|
* @param targets - records the user selected in the panel.
|