dsh-live-teams 0.1.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.
Files changed (69) hide show
  1. package/LICENSE +176 -0
  2. package/NOTICE +11 -0
  3. package/README.md +85 -0
  4. package/cordis.patch.yml +25 -0
  5. package/lib/binding.d.ts +18 -0
  6. package/lib/binding.js +42 -0
  7. package/lib/changed-paths.d.ts +26 -0
  8. package/lib/changed-paths.js +69 -0
  9. package/lib/client.js +6753 -0
  10. package/lib/command-queue.d.ts +60 -0
  11. package/lib/command-queue.js +185 -0
  12. package/lib/compatibility.js +109 -0
  13. package/lib/context-provider.d.ts +110 -0
  14. package/lib/context-provider.js +249 -0
  15. package/lib/dispatch.d.ts +174 -0
  16. package/lib/dispatch.js +624 -0
  17. package/lib/errors.d.ts +36 -0
  18. package/lib/errors.js +103 -0
  19. package/lib/git-artifacts.d.ts +50 -0
  20. package/lib/git-artifacts.js +242 -0
  21. package/lib/index.d.ts +14 -0
  22. package/lib/index.js +14 -0
  23. package/lib/mailbox.d.ts +274 -0
  24. package/lib/mailbox.js +721 -0
  25. package/lib/member-tools.d.ts +57 -0
  26. package/lib/member-tools.js +1265 -0
  27. package/lib/migrations.d.ts +17 -0
  28. package/lib/migrations.js +47 -0
  29. package/lib/plugin.d.ts +106 -0
  30. package/lib/plugin.js +1003 -0
  31. package/lib/roles.d.ts +35 -0
  32. package/lib/roles.js +284 -0
  33. package/lib/routes.d.ts +586 -0
  34. package/lib/routes.js +2816 -0
  35. package/lib/scope.d.ts +62 -0
  36. package/lib/scope.js +133 -0
  37. package/lib/session-bridge.d.ts +76 -0
  38. package/lib/session-bridge.js +147 -0
  39. package/lib/session-title.js +35 -0
  40. package/lib/storage.d.ts +9 -0
  41. package/lib/storage.js +65 -0
  42. package/lib/task-store.d.ts +729 -0
  43. package/lib/task-store.js +2205 -0
  44. package/lib/team-store.d.ts +216 -0
  45. package/lib/team-store.js +765 -0
  46. package/lib/tree-snapshot.d.ts +28 -0
  47. package/lib/tree-snapshot.js +80 -0
  48. package/lib/types/client/TeamView.d.ts +26 -0
  49. package/lib/types/client/TeamView.dom.test.d.ts +1 -0
  50. package/lib/types/client/api.d.ts +522 -0
  51. package/lib/types/client/api.test.d.ts +1 -0
  52. package/lib/types/client/attention.d.ts +65 -0
  53. package/lib/types/client/attention.test.d.ts +1 -0
  54. package/lib/types/client/index.d.ts +31 -0
  55. package/lib/types/client/locales.d.ts +577 -0
  56. package/lib/types/client/member-name.d.ts +14 -0
  57. package/lib/types/client/member-name.test.d.ts +1 -0
  58. package/lib/types/client/roster.d.ts +26 -0
  59. package/lib/types/client/roster.test.d.ts +1 -0
  60. package/lib/types/client/styles.d.ts +3 -0
  61. package/package.json +104 -0
  62. package/roles/builder.md +40 -0
  63. package/roles/delegate.md +36 -0
  64. package/roles/lead.md +46 -0
  65. package/roles/oracle.md +36 -0
  66. package/roles/researcher.md +37 -0
  67. package/roles/reviewer.md +45 -0
  68. package/roles/scout.md +36 -0
  69. package/roles/verifier.md +36 -0
package/lib/roles.d.ts ADDED
@@ -0,0 +1,35 @@
1
+ //#region src/roles.d.ts
2
+ export type RolePermissions = {
3
+ readonly workspaceWrite?: boolean;
4
+ readonly canDelegate?: boolean;
5
+ readonly [key: string]: boolean | undefined;
6
+ };
7
+ export type LiveTeamsRole = {
8
+ readonly id: string;
9
+ readonly name: string;
10
+ readonly body: string;
11
+ readonly description?: string;
12
+ readonly capabilities: readonly string[];
13
+ readonly preferredTaskKinds: readonly string[];
14
+ readonly restrictions: readonly string[];
15
+ readonly aliases: readonly string[];
16
+ readonly permissions: RolePermissions;
17
+ readonly source: 'global' | 'workspace';
18
+ readonly overridden: boolean;
19
+ readonly filePath: string;
20
+ };
21
+ export type ParsedRole = Omit<LiveTeamsRole, 'source' | 'overridden'>;
22
+ export type RoleRegistrySync = Pick<RoleRegistry, 'resolve'>;
23
+ export type RoleRegistry = {
24
+ readonly roles: readonly LiveTeamsRole[];
25
+ readonly globalRoleIds: ReadonlySet<string>;
26
+ readonly globalRolePath: (id: string) => string | undefined;
27
+ readonly globalDirectory: string;
28
+ readonly workspaceDirectory: string;
29
+ readonly workspaceDirectoryExists: boolean;
30
+ readonly resolve: (value: string) => LiveTeamsRole | undefined;
31
+ };
32
+ export declare function readRoleRegistrySync(globalDirectory: string, workspaceDirectory: string): RoleRegistry;
33
+ export declare function readRoleRegistry(globalDirectory: string, workspaceDirectory: string): Promise<RoleRegistry>;
34
+ export declare function parseRoleDocument(id: string, contents: string, filePath?: string): ParsedRole;
35
+ //#endregion
package/lib/roles.js ADDED
@@ -0,0 +1,284 @@
1
+ import path from "node:path";
2
+ import { promises, readFileSync, readdirSync, statSync } from "node:fs";
3
+ //#region src/roles.ts
4
+ function record(value) {
5
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : {};
6
+ }
7
+ function scalar(value) {
8
+ const trimmed = value.trim();
9
+ if (trimmed.length === 0) return void 0;
10
+ if (trimmed.startsWith("\"") && trimmed.endsWith("\"") || trimmed.startsWith("'") && trimmed.endsWith("'")) return trimmed.slice(1, -1);
11
+ if (trimmed === "true") return true;
12
+ if (trimmed === "false") return false;
13
+ return trimmed;
14
+ }
15
+ function scalarList(value) {
16
+ const trimmed = value.trim();
17
+ if (!trimmed.startsWith("[") || !trimmed.endsWith("]")) return void 0;
18
+ const inner = trimmed.slice(1, -1).trim();
19
+ if (inner.length === 0) return [];
20
+ const parts = [];
21
+ let current = "";
22
+ let quote;
23
+ for (const character of inner) if (quote !== void 0) {
24
+ current += character;
25
+ if (character === quote) quote = void 0;
26
+ } else if (character === "\"" || character === "'") {
27
+ quote = character;
28
+ current += character;
29
+ } else if (character === ",") {
30
+ const item = scalar(current);
31
+ if (typeof item !== "string") return void 0;
32
+ parts.push(item);
33
+ current = "";
34
+ } else current += character;
35
+ if (quote !== void 0) return void 0;
36
+ const item = scalar(current);
37
+ if (typeof item !== "string") return void 0;
38
+ parts.push(item);
39
+ return parts;
40
+ }
41
+ /** Parse the deliberately small, YAML-like role metadata format without executing YAML. */
42
+ function parseFrontMatter(lines) {
43
+ const result = {};
44
+ const permissions = {};
45
+ let inPermissions = false;
46
+ for (const line of lines) {
47
+ if (line.trim().length === 0 || line.trimStart().startsWith("#")) continue;
48
+ const match = /^(\s*)([A-Za-z][A-Za-z0-9_-]*):(?:\s*(.*))?$/u.exec(line);
49
+ if (match === null) return void 0;
50
+ const indent = match[1]?.length ?? 0;
51
+ const key = match[2];
52
+ const raw = match[3] ?? "";
53
+ if (key === void 0) return void 0;
54
+ if (indent > 0 && inPermissions) {
55
+ const value = scalar(raw);
56
+ if (typeof value !== "boolean") return void 0;
57
+ permissions[key] = value;
58
+ continue;
59
+ }
60
+ inPermissions = false;
61
+ if (key === "permissions") {
62
+ if (raw.trim().length > 0) return void 0;
63
+ inPermissions = true;
64
+ continue;
65
+ }
66
+ const list = scalarList(raw);
67
+ const value = scalar(raw);
68
+ if (key === "description") {
69
+ if (typeof value !== "string") return void 0;
70
+ result.description = value;
71
+ } else if (key === "capabilities") {
72
+ if (list === void 0) return void 0;
73
+ result.capabilities = list;
74
+ } else if (key === "preferredTaskKinds") {
75
+ if (list === void 0) return void 0;
76
+ result.preferredTaskKinds = list;
77
+ } else if (key === "restrictions") {
78
+ if (list !== void 0) result.restrictions = list;
79
+ else if (typeof value === "string") result.restrictions = [value];
80
+ else return void 0;
81
+ } else if (key === "aliases") {
82
+ if (list === void 0) return void 0;
83
+ result.aliases = list;
84
+ }
85
+ }
86
+ if (Object.keys(permissions).length > 0) result.permissions = permissions;
87
+ return result;
88
+ }
89
+ function splitRoleDocument(contents) {
90
+ const normalized = contents.replace(/^\uFEFF/u, "");
91
+ if (!normalized.startsWith("---\n") && !normalized.startsWith("---\r\n")) return {
92
+ metadata: void 0,
93
+ body: normalized
94
+ };
95
+ const lines = normalized.split(/\r?\n/u);
96
+ if (lines[0] !== "---") return {
97
+ metadata: void 0,
98
+ body: normalized
99
+ };
100
+ const closing = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
101
+ if (closing < 0) return {
102
+ metadata: void 0,
103
+ body: normalized
104
+ };
105
+ const metadata = parseFrontMatter(lines.slice(1, closing));
106
+ if (metadata === void 0) return {
107
+ metadata: void 0,
108
+ body: normalized
109
+ };
110
+ return {
111
+ metadata,
112
+ body: lines.slice(closing + 1).join("\n")
113
+ };
114
+ }
115
+ function firstHeading(body, fallback) {
116
+ for (const line of body.split(/\r?\n/u)) {
117
+ const match = /^#\s+(.+?)\s*#*\s*$/u.exec(line);
118
+ if (match?.[1] !== void 0 && match[1].trim().length > 0) return match[1].trim();
119
+ }
120
+ return fallback;
121
+ }
122
+ function roleId(file) {
123
+ const basename = path.basename(file);
124
+ if (!basename.endsWith(".md")) return void 0;
125
+ const id = basename.slice(0, -3);
126
+ return /^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(id) ? id : void 0;
127
+ }
128
+ async function readRole(file, source, overridden) {
129
+ const id = roleId(file);
130
+ if (id === void 0) return void 0;
131
+ let contents;
132
+ try {
133
+ contents = await promises.readFile(file, "utf8");
134
+ } catch {
135
+ return;
136
+ }
137
+ const document = splitRoleDocument(contents);
138
+ const metadata = document.metadata ?? {};
139
+ return {
140
+ id,
141
+ name: firstHeading(document.body, id),
142
+ body: document.body.trim(),
143
+ ...metadata.description === void 0 ? {} : { description: metadata.description },
144
+ capabilities: metadata.capabilities ?? [],
145
+ preferredTaskKinds: metadata.preferredTaskKinds ?? [],
146
+ restrictions: metadata.restrictions ?? [],
147
+ aliases: metadata.aliases ?? [],
148
+ permissions: metadata.permissions ?? {},
149
+ source,
150
+ overridden,
151
+ filePath: path.resolve(file)
152
+ };
153
+ }
154
+ async function readDirectory(directory, source, overriddenIds) {
155
+ let entries;
156
+ try {
157
+ entries = await promises.readdir(directory);
158
+ } catch (error) {
159
+ if (record(error).code === "ENOENT") return [];
160
+ return [];
161
+ }
162
+ const roles = [];
163
+ for (const entry of entries.sort()) {
164
+ const role = await readRole(path.join(directory, entry), source, source === "workspace" && overriddenIds.has(entry.slice(0, -3)));
165
+ if (role !== void 0) roles.push(role);
166
+ }
167
+ return roles;
168
+ }
169
+ function readDirectorySync(directory, source, overriddenIds) {
170
+ let entries;
171
+ try {
172
+ entries = readdirSync(directory);
173
+ } catch {
174
+ return [];
175
+ }
176
+ const roles = [];
177
+ for (const entry of entries.sort()) {
178
+ const file = path.join(directory, entry);
179
+ const id = roleId(file);
180
+ if (id === void 0) continue;
181
+ let contents;
182
+ try {
183
+ contents = readFileSync(file, "utf8");
184
+ } catch {
185
+ continue;
186
+ }
187
+ const document = splitRoleDocument(contents);
188
+ const metadata = document.metadata ?? {};
189
+ roles.push({
190
+ id,
191
+ name: firstHeading(document.body, id),
192
+ body: document.body.trim(),
193
+ ...metadata.description === void 0 ? {} : { description: metadata.description },
194
+ capabilities: metadata.capabilities ?? [],
195
+ preferredTaskKinds: metadata.preferredTaskKinds ?? [],
196
+ restrictions: metadata.restrictions ?? [],
197
+ aliases: metadata.aliases ?? [],
198
+ permissions: metadata.permissions ?? {},
199
+ source,
200
+ overridden: source === "workspace" && overriddenIds.has(id),
201
+ filePath: path.resolve(file)
202
+ });
203
+ }
204
+ return roles;
205
+ }
206
+ function readRoleRegistrySync(globalDirectory, workspaceDirectory) {
207
+ const globalPath = path.resolve(globalDirectory);
208
+ const workspacePath = path.resolve(workspaceDirectory);
209
+ const globalRoles = readDirectorySync(globalPath, "global", /* @__PURE__ */ new Set());
210
+ const globalIds = new Set(globalRoles.map((role) => role.id));
211
+ const workspaceRoles = readDirectorySync(workspacePath, "workspace", globalIds);
212
+ const workspaceIds = new Set(workspaceRoles.map((role) => role.id));
213
+ const roles = [...globalRoles.filter((role) => !workspaceIds.has(role.id)), ...workspaceRoles].sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
214
+ const byIdentifier = /* @__PURE__ */ new Map();
215
+ for (const role of roles) {
216
+ byIdentifier.set(role.id, role);
217
+ for (const alias of role.aliases) if (!byIdentifier.has(alias)) byIdentifier.set(alias, role);
218
+ }
219
+ const globalPaths = new Map(globalRoles.map((role) => [role.id, role.filePath]));
220
+ let workspaceDirectoryExists = false;
221
+ try {
222
+ workspaceDirectoryExists = statSync(workspacePath).isDirectory();
223
+ } catch {
224
+ workspaceDirectoryExists = false;
225
+ }
226
+ return {
227
+ roles,
228
+ globalRoleIds: globalIds,
229
+ globalRolePath: (id) => globalPaths.get(id),
230
+ globalDirectory: globalPath,
231
+ workspaceDirectory: workspacePath,
232
+ workspaceDirectoryExists,
233
+ resolve: (value) => byIdentifier.get(value)
234
+ };
235
+ }
236
+ async function readRoleRegistry(globalDirectory, workspaceDirectory) {
237
+ const globalPath = path.resolve(globalDirectory);
238
+ const workspacePath = path.resolve(workspaceDirectory);
239
+ const globalRoles = await readDirectory(globalPath, "global", /* @__PURE__ */ new Set());
240
+ const globalIds = new Set(globalRoles.map((role) => role.id));
241
+ const workspaceRoles = await readDirectory(workspacePath, "workspace", globalIds);
242
+ const workspaceIds = new Set(workspaceRoles.map((role) => role.id));
243
+ const roles = [...globalRoles.filter((role) => !workspaceIds.has(role.id)), ...workspaceRoles].sort((left, right) => left.name.localeCompare(right.name) || left.id.localeCompare(right.id));
244
+ const byIdentifier = /* @__PURE__ */ new Map();
245
+ for (const role of roles) {
246
+ byIdentifier.set(role.id, role);
247
+ for (const alias of role.aliases) if (!byIdentifier.has(alias)) byIdentifier.set(alias, role);
248
+ }
249
+ let workspaceDirectoryExists = false;
250
+ try {
251
+ workspaceDirectoryExists = (await promises.stat(workspacePath)).isDirectory();
252
+ } catch {
253
+ workspaceDirectoryExists = false;
254
+ }
255
+ const globalPaths = new Map(globalRoles.map((role) => [role.id, role.filePath]));
256
+ return {
257
+ roles,
258
+ globalRoleIds: globalIds,
259
+ globalRolePath: (id) => globalPaths.get(id),
260
+ globalDirectory: globalPath,
261
+ workspaceDirectory: workspacePath,
262
+ workspaceDirectoryExists,
263
+ resolve: (value) => byIdentifier.get(value)
264
+ };
265
+ }
266
+ function parseRoleDocument(id, contents, filePath = id) {
267
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/u.test(id)) throw new Error("invalid role id");
268
+ const document = splitRoleDocument(contents);
269
+ const metadata = document.metadata ?? {};
270
+ return {
271
+ id,
272
+ name: firstHeading(document.body, id),
273
+ body: document.body.trim(),
274
+ ...metadata.description === void 0 ? {} : { description: metadata.description },
275
+ capabilities: metadata.capabilities ?? [],
276
+ preferredTaskKinds: metadata.preferredTaskKinds ?? [],
277
+ restrictions: metadata.restrictions ?? [],
278
+ aliases: metadata.aliases ?? [],
279
+ permissions: metadata.permissions ?? {},
280
+ filePath: path.resolve(filePath)
281
+ };
282
+ }
283
+ //#endregion
284
+ export { parseRoleDocument, readRoleRegistry, readRoleRegistrySync };