@stofone/dsh-agent-roster 0.12.0 → 0.13.1

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.
Files changed (34) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +19 -10
  3. package/README.zh.md +18 -10
  4. package/client.js +334 -165
  5. package/examples/dist/core-review-1.0.0.json +9 -0
  6. package/examples/dist/research-0.1.0.json +7 -0
  7. package/examples-orchestration/dist/core-review-1.0.0.json +9 -0
  8. package/index.js +104 -66
  9. package/lib/compile2.js +13 -4
  10. package/locale/en.json +11 -8
  11. package/locale/zh.json +11 -8
  12. package/orgs/agent-roster-collective/dist/api-design-1.0.0.json +10 -0
  13. package/orgs/agent-roster-collective/dist/content-ops-1.0.0.json +9 -0
  14. package/orgs/agent-roster-collective/dist/contract-review-1.0.0.json +9 -0
  15. package/orgs/agent-roster-collective/dist/data-quality-1.0.0.json +10 -0
  16. package/orgs/agent-roster-collective/dist/db-migration-1.0.0.json +10 -0
  17. package/orgs/agent-roster-collective/dist/docs-writer-1.0.0.json +9 -0
  18. package/orgs/agent-roster-collective/dist/finance-recon-1.0.0.json +9 -0
  19. package/orgs/agent-roster-collective/dist/frontend-ui-1.0.0.json +10 -0
  20. package/orgs/agent-roster-collective/dist/i18n-l10n-1.0.0.json +10 -0
  21. package/orgs/agent-roster-collective/dist/incident-response-1.0.0.json +10 -0
  22. package/orgs/agent-roster-collective/dist/literature-review-1.0.0.json +9 -0
  23. package/orgs/agent-roster-collective/dist/market-research-1.0.0.json +9 -0
  24. package/orgs/agent-roster-collective/dist/ml-eval-1.0.0.json +9 -0
  25. package/orgs/agent-roster-collective/dist/performance-1.0.0.json +9 -0
  26. package/orgs/agent-roster-collective/dist/product-discovery-1.0.0.json +9 -0
  27. package/orgs/agent-roster-collective/dist/qa-regression-1.0.0.json +10 -0
  28. package/orgs/agent-roster-collective/dist/recruiting-1.0.0.json +9 -0
  29. package/orgs/agent-roster-collective/dist/release-manager-1.0.0.json +9 -0
  30. package/orgs/agent-roster-collective/dist/security-audit-1.0.0.json +10 -0
  31. package/orgs/agent-roster-collective/dist/sre-oncall-1.0.0.json +10 -0
  32. package/orgs/agent-roster-collective/dist/support-triage-1.0.0.json +9 -0
  33. package/package.json +1 -1
  34. package/schema/compiled.v1.schema.json +30 -0
package/index.js CHANGED
@@ -174,8 +174,107 @@ export function apply(ctx, config) {
174
174
  "collection route",
175
175
  );
176
176
 
177
- // 搜索是「找一份档案」的入口:一次请求横跨本机与所有订阅来源。
178
- // 远端的拉取复用 collection 的 60s 缓存,所以连续输入不会对同一个集合重复打网络。
177
+ // 「档案库」与「搜索」共用同一次摊平动作:本机 + 所有订阅来源。
178
+ // 两条路由各写一遍,缓存口径、并发上限与诊断文案迟早会分叉。
179
+ const gatherEntries = async ({ refresh = false } = {}) => {
180
+ const library = collectLibrary(ctx, settings, { name: pkg.name, version: pkg.version });
181
+ const entries = [];
182
+ let localCount = 0;
183
+ for (const workspace of library.workspaces) {
184
+ for (const roster of workspace.rosters) {
185
+ entries.push({ scope: "local", roster, libraryId: workspace.rootDir ?? null, file: roster.file ?? null });
186
+ localCount += 1;
187
+ }
188
+ }
189
+
190
+ const diagnostics = [];
191
+ const collections = [];
192
+ try {
193
+ const remote = await loadRemotePanel(settings, { refresh });
194
+ for (const item of remote.diagnostics ?? []) if (item.severity !== "info") diagnostics.push(item);
195
+ for (const collection of remote.collections ?? []) collections.push(collection);
196
+ } catch (error) {
197
+ diagnostics.push({
198
+ severity: "warn",
199
+ code: "index-unavailable",
200
+ message: `订阅索引拉不到,这次只显示本机档案:${error instanceof Error ? error.message : String(error)}`,
201
+ });
202
+ }
203
+
204
+ // 来源两两并发:一个慢集合不该把所有来源都拖住,也不该一次打三十个请求。
205
+ const sourceConcurrency = 2;
206
+ let cursor = 0;
207
+ const fetchNext = async () => {
208
+ while (cursor < collections.length) {
209
+ const collection = collections[cursor];
210
+ cursor += 1;
211
+ const key = `${collection.source ?? ""}::${collection.id}`;
212
+ try {
213
+ const cached = collectionCache.get(key);
214
+ const fetched = cached ?? collectionCache.set(key, await fetchCollection(collection));
215
+ for (const roster of fetched.rosters ?? []) {
216
+ entries.push({
217
+ scope: "remote",
218
+ roster,
219
+ collectionId: collection.id,
220
+ collectionTitle: collection.title ?? null,
221
+ file: roster.file ?? null,
222
+ });
223
+ }
224
+ for (const item of fetched.diagnostics ?? []) if (item.severity === "error") diagnostics.push(item);
225
+ } catch (error) {
226
+ diagnostics.push({
227
+ severity: "warn",
228
+ code: "source-failed",
229
+ message: `${collection.id} 拉取失败:${error instanceof Error ? error.message : String(error)}`,
230
+ });
231
+ }
232
+ }
233
+ };
234
+ await Promise.all(Array.from({ length: Math.min(sourceConcurrency, collections.length) }, fetchNext));
235
+
236
+ return { entries, diagnostics: diagnostics.slice(0, 20), counts: { local: localCount, remote: entries.length - localCount } };
237
+ };
238
+
239
+ const catalogEntry = (entry) => {
240
+ const roster = entry.roster;
241
+ return {
242
+ scope: entry.scope,
243
+ rosterId: typeof roster.id === "string" ? roster.id : "",
244
+ title: typeof roster.title === "string" ? roster.title : "",
245
+ description: typeof roster.description === "string" ? roster.description : "",
246
+ tags: Array.isArray(roster.tags) ? roster.tags : [],
247
+ members: Array.isArray(roster.members) ? roster.members.length : 0,
248
+ memberRoles: (Array.isArray(roster.members) ? roster.members : [])
249
+ .map((member) => (typeof member?.role === "string" ? member.role : ""))
250
+ .filter((role) => role !== "")
251
+ .slice(0, 4),
252
+ libraryId: entry.libraryId ?? null,
253
+ collectionId: entry.collectionId ?? null,
254
+ collectionTitle: entry.collectionTitle ?? null,
255
+ file: entry.file ?? null,
256
+ };
257
+ };
258
+
259
+ // 档案库:像插件市场那样「全部摊开」,供浏览、按标签筛选、按来源聚合。
260
+ register(
261
+ "/plugins/dsh-agent-roster/catalog",
262
+ async (req, res) => {
263
+ if (guard(req, res)) return;
264
+ if (!requireMethod(req, res, "GET")) return;
265
+ const query = new URL(req.url ?? "/", "http://localhost").searchParams;
266
+ const gathered = await gatherEntries({ refresh: query.get("refresh") === "1" });
267
+ sendJson(res, 200, {
268
+ ok: true,
269
+ entries: gathered.entries.map(catalogEntry),
270
+ counts: gathered.counts,
271
+ diagnostics: gathered.diagnostics,
272
+ });
273
+ },
274
+ "catalog route",
275
+ );
276
+
277
+ // 搜索:同一个「摊平」结果上做排序,回车即可打开。空查询不做无谓的摊平。
179
278
  register(
180
279
  "/plugins/dsh-agent-roster/search",
181
280
  async (req, res) => {
@@ -187,70 +286,9 @@ export function apply(ctx, config) {
187
286
  sendJson(res, 200, { ok: true, q: "", tokens: [], total: 0, matches: [], counts: { local: 0, remote: 0 }, diagnostics: [] });
188
287
  return;
189
288
  }
190
-
191
- const library = collectLibrary(ctx, settings, { name: pkg.name, version: pkg.version });
192
- const entries = [];
193
- let localCount = 0;
194
- for (const workspace of library.workspaces) {
195
- for (const roster of workspace.rosters) {
196
- entries.push({ scope: "local", roster, libraryId: workspace.rootDir ?? null, file: roster.file ?? null });
197
- localCount += 1;
198
- }
199
- }
200
-
201
- const diagnostics = [];
202
- const collections = [];
203
- try {
204
- const remote = await loadRemotePanel(settings, {});
205
- for (const item of remote.diagnostics ?? []) if (item.severity !== "info") diagnostics.push(item);
206
- for (const collection of remote.collections ?? []) collections.push(collection);
207
- } catch (error) {
208
- diagnostics.push({
209
- severity: "warn",
210
- code: "search-index-unavailable",
211
- message: `订阅索引拉不到,本次只搜索本机档案:${error instanceof Error ? error.message : String(error)}`,
212
- });
213
- }
214
-
215
- // 来源两两并发:一个慢集合不该把所有来源都拖住,也不该一次打三十个请求。
216
- const sourceConcurrency = 2;
217
- let cursor = 0;
218
- const fetchNext = async () => {
219
- while (cursor < collections.length) {
220
- const collection = collections[cursor];
221
- cursor += 1;
222
- const key = `${collection.source ?? ""}::${collection.id}`;
223
- try {
224
- const cached = collectionCache.get(key);
225
- const fetched = cached ?? collectionCache.set(key, await fetchCollection(collection));
226
- for (const roster of fetched.rosters ?? []) {
227
- entries.push({
228
- scope: "remote",
229
- roster,
230
- collectionId: collection.id,
231
- collectionTitle: collection.title ?? null,
232
- file: roster.file ?? null,
233
- });
234
- }
235
- for (const item of fetched.diagnostics ?? []) if (item.severity === "error") diagnostics.push(item);
236
- } catch (error) {
237
- diagnostics.push({
238
- severity: "warn",
239
- code: "search-source-failed",
240
- message: `${collection.id} 拉取失败:${error instanceof Error ? error.message : String(error)}`,
241
- });
242
- }
243
- }
244
- };
245
- await Promise.all(Array.from({ length: Math.min(sourceConcurrency, collections.length) }, fetchNext));
246
-
247
- const result = searchRosters(q, entries);
248
- const remoteCount = entries.length - localCount;
249
- sendJson(res, 200, {
250
- ...result,
251
- counts: { local: localCount, remote: remoteCount },
252
- diagnostics: diagnostics.slice(0, 20),
253
- });
289
+ const gathered = await gatherEntries();
290
+ const result = searchRosters(q, gathered.entries);
291
+ sendJson(res, 200, { ...result, counts: gathered.counts, diagnostics: gathered.diagnostics });
254
292
  },
255
293
  "search route",
256
294
  );
package/lib/compile2.js CHANGED
@@ -231,7 +231,13 @@ export function compileRoster(options) {
231
231
  source: {
232
232
  roster: roster.id,
233
233
  version: roster.version,
234
+ // 产物是唯一被分发的形态:发现它所需的一切(标题、标签、许可、维护者)必须自带,
235
+ // 否则"从远处找一份档案"就只剩一个 id。
234
236
  title: roster.title ?? null,
237
+ description: roster.description ?? null,
238
+ tags: Array.isArray(roster.tags) ? roster.tags : [],
239
+ license: roster.license ?? null,
240
+ maintainers: Array.isArray(roster.maintainers) ? roster.maintainers : [],
235
241
  repo: options.source?.repo ?? null,
236
242
  ref: options.source?.ref ?? null,
237
243
  lock: options.source?.lock ?? null,
@@ -510,10 +516,10 @@ export function rosterViewOf(compiled, roster2, options = {}) {
510
516
  version: compiled.source.version,
511
517
  file: compiled.source.file ?? null,
512
518
  title: roster2?.title ?? compiled.source.title ?? "",
513
- description: roster2?.description ?? "",
514
- license: roster2?.license ?? "",
515
- maintainers: roster2?.maintainers ?? [],
516
- tags: roster2?.tags ?? [],
519
+ description: roster2?.description ?? compiled.source.description ?? "",
520
+ license: roster2?.license ?? compiled.source.license ?? "",
521
+ maintainers: roster2?.maintainers ?? compiled.source.maintainers ?? [],
522
+ tags: roster2?.tags ?? compiled.source.tags ?? [],
517
523
  notes: roster2?.notes ?? null,
518
524
  members,
519
525
  captain,
@@ -618,6 +624,9 @@ export function flattenCompiledToRoster(compiled) {
618
624
  };
619
625
  if (source.title) roster.title = source.title;
620
626
  if (source.description) roster.description = source.description;
627
+ if (source.license) roster.license = source.license;
628
+ if (Array.isArray(source.maintainers) && source.maintainers.length > 0) roster.maintainers = source.maintainers;
629
+ if (Array.isArray(source.tags) && source.tags.length > 0) roster.tags = source.tags;
621
630
  if (compiled.captain) {
622
631
  const captain = { name: String(compiled.captain.role ?? "").replace(/@[^@]*$/, "") };
623
632
  if (compiled.captain.provider) captain.provider = compiled.captain.provider;
package/locale/en.json CHANGED
@@ -64,7 +64,8 @@
64
64
  "metaRosters": "rosters",
65
65
  "metaMembers": "members",
66
66
  "metaTeams": "teams",
67
- "metaOrganization": "Organization {name}"
67
+ "metaOrganization": "Organization {name}",
68
+ "tabsCatalog": "Library"
68
69
  },
69
70
  "roster": {
70
71
  "members": "{count} members",
@@ -302,18 +303,20 @@
302
303
  "source.filter": "Filter this collection",
303
304
  "source.filterPlaceholder": "Filter by id, title or member",
304
305
  "source.filterCount": "{shown} of {total}",
305
- "lookup": {
306
+ "catalog": {
306
307
  "placeholder": "What should it do? e.g. data quality, release, review",
307
- "hint": "Searches this machine and every subscribed source",
308
- "clear": "Clear",
309
308
  "searching": "Searching…",
310
- "counts": "{total} matched · candidates: {local} local · {remote} remote",
311
- "empty": "Nothing matched “{q}”. Try another wording, or check Sources for a subscription that has it.",
312
- "failed": "Search failed: the host did not answer.",
309
+ "counts": "{shown} / {total} · {local} local · {remote} remote",
310
+ "empty": "Nothing matches. Try another wording, or check Sources for a subscription.",
311
+ "failed": "The library did not load: the host did not answer.",
313
312
  "members": "{count} members",
314
313
  "local": "local",
315
314
  "remoteFrom": "from {collection}",
316
315
  "open": "Open",
317
- "pull": "Pull"
316
+ "pull": "Pull",
317
+ "mode": "Layout",
318
+ "modeSource": "By source",
319
+ "modeFlat": "Flat",
320
+ "refresh": "Refresh"
318
321
  }
319
322
  }
package/locale/zh.json CHANGED
@@ -64,7 +64,8 @@
64
64
  "metaRosters": "份档案",
65
65
  "metaMembers": "名成员",
66
66
  "metaTeams": "支团队",
67
- "metaOrganization": "组织 {name}"
67
+ "metaOrganization": "组织 {name}",
68
+ "tabsCatalog": "档案库"
68
69
  },
69
70
  "roster": {
70
71
  "members": "{count} 名成员",
@@ -302,18 +303,20 @@
302
303
  "source.filter": "在这份集合里查找",
303
304
  "source.filterPlaceholder": "按 id、标题或成员名过滤",
304
305
  "source.filterCount": "{shown} / {total}",
305
- "lookup": {
306
+ "catalog": {
306
307
  "placeholder": "要找什么能力?例:数据质量、发版、代码评审",
307
- "hint": "同时搜本机与已订阅的来源",
308
- "clear": "清除",
309
308
  "searching": "正在搜索…",
310
- "counts": "命中 {total} 份 · 候选:本机 {local} · 远端 {remote}",
311
- "empty": "没有找到「{q}」。换个说法,或到「档案源」看看是否订阅了它。",
312
- "failed": "搜索失败:宿主没有响应。",
309
+ "counts": "{shown} / {total} 份 · 本机 {local} · 远端 {remote}",
310
+ "empty": "没有符合条件的档案。换个词,或去「档案源」看看订阅。",
311
+ "failed": "档案库加载失败:宿主没有响应。",
313
312
  "members": "{count} 名成员",
314
313
  "local": "本机",
315
314
  "remoteFrom": "来自 {collection}",
316
315
  "open": "打开",
317
- "pull": "拉取"
316
+ "pull": "拉取",
317
+ "mode": "显示方式",
318
+ "modeSource": "按来源",
319
+ "modeFlat": "平铺",
320
+ "refresh": "刷新"
318
321
  }
319
322
  }
@@ -4,6 +4,16 @@
4
4
  "roster": "api-design",
5
5
  "version": "1.0.0",
6
6
  "title": "API 设计评审组",
7
+ "description": "在写第一行实现之前,把接口的形状、演进与失败语义定下来。",
8
+ "tags": [
9
+ "api",
10
+ "design",
11
+ "review"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "content-ops",
5
5
  "version": "1.0.0",
6
6
  "title": "内容运营组",
7
+ "description": "一次发布:选题、成稿、分发,三件事各有验收。",
8
+ "tags": [
9
+ "content",
10
+ "publishing"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "contract-review",
5
5
  "version": "1.0.0",
6
6
  "title": "合同审阅组",
7
+ "description": "找到会让你赔钱、被锁定或无法退出的条款。",
8
+ "tags": [
9
+ "legal",
10
+ "contract"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "data-quality",
5
5
  "version": "1.0.0",
6
6
  "title": "数据质量组",
7
+ "description": "为一份数据表定“什么算合格”,并把不合格变成可复现的判据。",
8
+ "tags": [
9
+ "data",
10
+ "quality",
11
+ "checks"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "db-migration",
5
5
  "version": "1.0.0",
6
6
  "title": "数据库迁移组",
7
+ "description": "把一次 schema 变更做到可回滚:先证明能回,再动手。",
8
+ "tags": [
9
+ "database",
10
+ "migration",
11
+ "reliability"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "docs-writer",
5
5
  "version": "1.0.0",
6
6
  "title": "文档组",
7
+ "description": "让文档里的每条命令都真的能跑,每个读数都带时间。",
8
+ "tags": [
9
+ "docs",
10
+ "writing"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "finance-recon",
5
5
  "version": "1.0.0",
6
6
  "title": "财务对账组",
7
+ "description": "让两套账的差异落到具体行,而不是“大概对得上”。",
8
+ "tags": [
9
+ "finance",
10
+ "reconciliation"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "frontend-ui",
5
5
  "version": "1.0.0",
6
6
  "title": "前端实现组",
7
+ "description": "把设计做成能用、能读、能被键盘与读屏器操作的东西。",
8
+ "tags": [
9
+ "frontend",
10
+ "ui",
11
+ "a11y"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "i18n-l10n",
5
5
  "version": "1.0.0",
6
6
  "title": "本地化组",
7
+ "description": "把一份文案做成两种语言,且两种语言说同一件事。",
8
+ "tags": [
9
+ "i18n",
10
+ "docs",
11
+ "review"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "incident-response",
5
5
  "version": "1.0.0",
6
6
  "title": "事故响应组",
7
+ "description": "线上出事时的三个人:指挥、定位、记录;先止损再找根因。",
8
+ "tags": [
9
+ "incident",
10
+ "oncall",
11
+ "reliability"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "literature-review",
5
5
  "version": "1.0.0",
6
6
  "title": "文献综述组",
7
+ "description": "把一堆论文读成一张能追到出处的证据表。",
8
+ "tags": [
9
+ "research",
10
+ "literature"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "market-research",
5
5
  "version": "1.0.0",
6
6
  "title": "市场研究组",
7
+ "description": "把“听说”变成“有出处的数”,并说清它什么时候会失效。",
8
+ "tags": [
9
+ "research",
10
+ "market"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "ml-eval",
5
5
  "version": "1.0.0",
6
6
  "title": "模型评测组",
7
+ "description": "跑分之前先问:这套题能不能区分好坏。",
8
+ "tags": [
9
+ "ml",
10
+ "evaluation"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "performance",
5
5
  "version": "1.0.0",
6
6
  "title": "性能优化组",
7
+ "description": "先量再改:没有基线就没有优化。",
8
+ "tags": [
9
+ "performance",
10
+ "profiling"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "product-discovery",
5
5
  "version": "1.0.0",
6
6
  "title": "产品发现组",
7
+ "description": "在写需求之前,先证明这个需求真的存在。",
8
+ "tags": [
9
+ "product",
10
+ "discovery"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "qa-regression",
5
5
  "version": "1.0.0",
6
6
  "title": "回归测试组",
7
+ "description": "每次改动都问:哪些旧行为可能被碰坏。",
8
+ "tags": [
9
+ "qa",
10
+ "regression",
11
+ "testing"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "recruiting",
5
5
  "version": "1.0.0",
6
6
  "title": "招聘筛选组",
7
+ "description": "按岗位要求筛人,只报证据,不做印象判断。",
8
+ "tags": [
9
+ "hiring",
10
+ "screening"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "release-manager",
5
5
  "version": "1.0.0",
6
6
  "title": "发布管理组",
7
+ "description": "每一次发布都可复现、可回滚、可解释。",
8
+ "tags": [
9
+ "release",
10
+ "versioning"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "security-audit",
5
5
  "version": "1.0.0",
6
6
  "title": "安全审计组",
7
+ "description": "在代码里找“能被利用”的东西,而不是“看着不安全”的东西。",
8
+ "tags": [
9
+ "security",
10
+ "audit",
11
+ "review"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,16 @@
4
4
  "roster": "sre-oncall",
5
5
  "version": "1.0.0",
6
6
  "title": "SRE 值守组",
7
+ "description": "值守不是盯屏:定告警阈值、写手册、算容量。",
8
+ "tags": [
9
+ "sre",
10
+ "oncall",
11
+ "capacity"
12
+ ],
13
+ "license": "MIT",
14
+ "maintainers": [
15
+ "STofone"
16
+ ],
7
17
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
18
  "ref": "main",
9
19
  "lock": null,
@@ -4,6 +4,15 @@
4
4
  "roster": "support-triage",
5
5
  "version": "1.0.0",
6
6
  "title": "客服分诊组",
7
+ "description": "把每条来信分到“能自助、要人接、是缺陷”三条路之一。",
8
+ "tags": [
9
+ "support",
10
+ "triage"
11
+ ],
12
+ "license": "MIT",
13
+ "maintainers": [
14
+ "STofone"
15
+ ],
7
16
  "repo": "dsh-agent-roster/dsh-agent-roster",
8
17
  "ref": "main",
9
18
  "lock": null,