@laurentenhoor/devclaw 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 (163) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +406 -0
  3. package/dist/index.d.ts +88 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +107 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/lib/audit.d.ts +2 -0
  8. package/dist/lib/audit.d.ts.map +1 -0
  9. package/dist/lib/audit.js +42 -0
  10. package/dist/lib/audit.js.map +1 -0
  11. package/dist/lib/binding-manager.d.ts +35 -0
  12. package/dist/lib/binding-manager.d.ts.map +1 -0
  13. package/dist/lib/binding-manager.js +88 -0
  14. package/dist/lib/binding-manager.js.map +1 -0
  15. package/dist/lib/cli.d.ts +12 -0
  16. package/dist/lib/cli.d.ts.map +1 -0
  17. package/dist/lib/cli.js +69 -0
  18. package/dist/lib/cli.js.map +1 -0
  19. package/dist/lib/dispatch.d.ts +58 -0
  20. package/dist/lib/dispatch.d.ts.map +1 -0
  21. package/dist/lib/dispatch.js +163 -0
  22. package/dist/lib/dispatch.js.map +1 -0
  23. package/dist/lib/model-selector.d.ts +21 -0
  24. package/dist/lib/model-selector.d.ts.map +1 -0
  25. package/dist/lib/model-selector.js +74 -0
  26. package/dist/lib/model-selector.js.map +1 -0
  27. package/dist/lib/notify.d.ts +54 -0
  28. package/dist/lib/notify.d.ts.map +1 -0
  29. package/dist/lib/notify.js +143 -0
  30. package/dist/lib/notify.js.map +1 -0
  31. package/dist/lib/onboarding.d.ts +5 -0
  32. package/dist/lib/onboarding.d.ts.map +1 -0
  33. package/dist/lib/onboarding.js +124 -0
  34. package/dist/lib/onboarding.js.map +1 -0
  35. package/dist/lib/projects.d.ts +64 -0
  36. package/dist/lib/projects.d.ts.map +1 -0
  37. package/dist/lib/projects.js +127 -0
  38. package/dist/lib/projects.js.map +1 -0
  39. package/dist/lib/providers/github.d.ts +23 -0
  40. package/dist/lib/providers/github.d.ts.map +1 -0
  41. package/dist/lib/providers/github.js +130 -0
  42. package/dist/lib/providers/github.js.map +1 -0
  43. package/dist/lib/providers/gitlab.d.ts +23 -0
  44. package/dist/lib/providers/gitlab.d.ts.map +1 -0
  45. package/dist/lib/providers/gitlab.js +133 -0
  46. package/dist/lib/providers/gitlab.js.map +1 -0
  47. package/dist/lib/providers/index.d.ts +12 -0
  48. package/dist/lib/providers/index.d.ts.map +1 -0
  49. package/dist/lib/providers/index.js +25 -0
  50. package/dist/lib/providers/index.js.map +1 -0
  51. package/dist/lib/providers/provider.d.ts +35 -0
  52. package/dist/lib/providers/provider.d.ts.map +1 -0
  53. package/dist/lib/providers/provider.js +13 -0
  54. package/dist/lib/providers/provider.js.map +1 -0
  55. package/dist/lib/services/health.d.ts +38 -0
  56. package/dist/lib/services/health.d.ts.map +1 -0
  57. package/dist/lib/services/health.js +100 -0
  58. package/dist/lib/services/health.js.map +1 -0
  59. package/dist/lib/services/heartbeat.d.ts +38 -0
  60. package/dist/lib/services/heartbeat.d.ts.map +1 -0
  61. package/dist/lib/services/heartbeat.js +199 -0
  62. package/dist/lib/services/heartbeat.js.map +1 -0
  63. package/dist/lib/services/pipeline.d.ts +36 -0
  64. package/dist/lib/services/pipeline.d.ts.map +1 -0
  65. package/dist/lib/services/pipeline.js +90 -0
  66. package/dist/lib/services/pipeline.js.map +1 -0
  67. package/dist/lib/services/queue.d.ts +14 -0
  68. package/dist/lib/services/queue.d.ts.map +1 -0
  69. package/dist/lib/services/queue.js +31 -0
  70. package/dist/lib/services/queue.js.map +1 -0
  71. package/dist/lib/services/tick.d.ts +62 -0
  72. package/dist/lib/services/tick.d.ts.map +1 -0
  73. package/dist/lib/services/tick.js +160 -0
  74. package/dist/lib/services/tick.js.map +1 -0
  75. package/dist/lib/setup/agent.d.ts +14 -0
  76. package/dist/lib/setup/agent.d.ts.map +1 -0
  77. package/dist/lib/setup/agent.js +72 -0
  78. package/dist/lib/setup/agent.js.map +1 -0
  79. package/dist/lib/setup/config.d.ts +22 -0
  80. package/dist/lib/setup/config.d.ts.map +1 -0
  81. package/dist/lib/setup/config.js +67 -0
  82. package/dist/lib/setup/config.js.map +1 -0
  83. package/dist/lib/setup/index.d.ts +53 -0
  84. package/dist/lib/setup/index.d.ts.map +1 -0
  85. package/dist/lib/setup/index.js +68 -0
  86. package/dist/lib/setup/index.js.map +1 -0
  87. package/dist/lib/setup/workspace.d.ts +6 -0
  88. package/dist/lib/setup/workspace.d.ts.map +1 -0
  89. package/dist/lib/setup/workspace.js +69 -0
  90. package/dist/lib/setup/workspace.js.map +1 -0
  91. package/dist/lib/templates.d.ts +9 -0
  92. package/dist/lib/templates.d.ts.map +1 -0
  93. package/dist/lib/templates.js +163 -0
  94. package/dist/lib/templates.js.map +1 -0
  95. package/dist/lib/tiers.d.ts +55 -0
  96. package/dist/lib/tiers.d.ts.map +1 -0
  97. package/dist/lib/tiers.js +74 -0
  98. package/dist/lib/tiers.js.map +1 -0
  99. package/dist/lib/tool-helpers.d.ts +44 -0
  100. package/dist/lib/tool-helpers.d.ts.map +1 -0
  101. package/dist/lib/tool-helpers.js +65 -0
  102. package/dist/lib/tool-helpers.js.map +1 -0
  103. package/dist/lib/tools/health.d.ts +28 -0
  104. package/dist/lib/tools/health.d.ts.map +1 -0
  105. package/dist/lib/tools/health.js +61 -0
  106. package/dist/lib/tools/health.js.map +1 -0
  107. package/dist/lib/tools/onboard.d.ts +24 -0
  108. package/dist/lib/tools/onboard.d.ts.map +1 -0
  109. package/dist/lib/tools/onboard.js +27 -0
  110. package/dist/lib/tools/onboard.js.map +1 -0
  111. package/dist/lib/tools/project-register.d.ts +51 -0
  112. package/dist/lib/tools/project-register.d.ts.map +1 -0
  113. package/dist/lib/tools/project-register.js +172 -0
  114. package/dist/lib/tools/project-register.js.map +1 -0
  115. package/dist/lib/tools/queue-status.test.d.ts +2 -0
  116. package/dist/lib/tools/queue-status.test.d.ts.map +1 -0
  117. package/dist/lib/tools/queue-status.test.js +48 -0
  118. package/dist/lib/tools/queue-status.test.js.map +1 -0
  119. package/dist/lib/tools/setup.d.ts +76 -0
  120. package/dist/lib/tools/setup.d.ts.map +1 -0
  121. package/dist/lib/tools/setup.js +102 -0
  122. package/dist/lib/tools/setup.js.map +1 -0
  123. package/dist/lib/tools/status.d.ts +24 -0
  124. package/dist/lib/tools/status.d.ts.map +1 -0
  125. package/dist/lib/tools/status.js +53 -0
  126. package/dist/lib/tools/status.js.map +1 -0
  127. package/dist/lib/tools/task-comment.d.ts +40 -0
  128. package/dist/lib/tools/task-comment.d.ts.map +1 -0
  129. package/dist/lib/tools/task-comment.js +84 -0
  130. package/dist/lib/tools/task-comment.js.map +1 -0
  131. package/dist/lib/tools/task-create.d.ts +54 -0
  132. package/dist/lib/tools/task-create.d.ts.map +1 -0
  133. package/dist/lib/tools/task-create.js +77 -0
  134. package/dist/lib/tools/task-create.js.map +1 -0
  135. package/dist/lib/tools/task-update.d.ts +40 -0
  136. package/dist/lib/tools/task-update.d.ts.map +1 -0
  137. package/dist/lib/tools/task-update.js +79 -0
  138. package/dist/lib/tools/task-update.js.map +1 -0
  139. package/dist/lib/tools/task-update.test.d.ts +7 -0
  140. package/dist/lib/tools/task-update.test.d.ts.map +1 -0
  141. package/dist/lib/tools/task-update.test.js +55 -0
  142. package/dist/lib/tools/task-update.test.js.map +1 -0
  143. package/dist/lib/tools/work-finish.d.ts +43 -0
  144. package/dist/lib/tools/work-finish.d.ts.map +1 -0
  145. package/dist/lib/tools/work-finish.js +77 -0
  146. package/dist/lib/tools/work-finish.js.map +1 -0
  147. package/dist/lib/tools/work-start.d.ts +39 -0
  148. package/dist/lib/tools/work-start.d.ts.map +1 -0
  149. package/dist/lib/tools/work-start.js +129 -0
  150. package/dist/lib/tools/work-start.js.map +1 -0
  151. package/dist/lib/types.d.ts +17 -0
  152. package/dist/lib/types.d.ts.map +1 -0
  153. package/dist/lib/types.js +8 -0
  154. package/dist/lib/types.js.map +1 -0
  155. package/docs/ARCHITECTURE.md +662 -0
  156. package/docs/CONFIGURATION.md +336 -0
  157. package/docs/MANAGEMENT.md +120 -0
  158. package/docs/ONBOARDING.md +251 -0
  159. package/docs/QA_WORKFLOW.md +120 -0
  160. package/docs/ROADMAP.md +96 -0
  161. package/docs/TESTING.md +339 -0
  162. package/docs/TOOLS.md +361 -0
  163. package/package.json +55 -0
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Append-only NDJSON audit logging.
3
+ * Every tool call automatically logs — no manual action needed from agents.
4
+ * Automatically truncates log to keep only last 250 lines.
5
+ */
6
+ import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
7
+ import { join, dirname } from "node:path";
8
+ const MAX_LOG_LINES = 50;
9
+ export async function log(workspaceDir, event, data) {
10
+ const filePath = join(workspaceDir, "log", "audit.log");
11
+ const entry = JSON.stringify({
12
+ ts: new Date().toISOString(),
13
+ event,
14
+ ...data,
15
+ });
16
+ try {
17
+ await appendFile(filePath, entry + "\n");
18
+ await truncateIfNeeded(filePath);
19
+ }
20
+ catch (err) {
21
+ // If directory doesn't exist, create it and retry
22
+ if (err.code === "ENOENT") {
23
+ await mkdir(dirname(filePath), { recursive: true });
24
+ await appendFile(filePath, entry + "\n");
25
+ }
26
+ // Audit logging should never break the tool — silently ignore other errors
27
+ }
28
+ }
29
+ async function truncateIfNeeded(filePath) {
30
+ try {
31
+ const content = await readFile(filePath, "utf-8");
32
+ const lines = content.split("\n").filter((line) => line.length > 0);
33
+ if (lines.length > MAX_LOG_LINES) {
34
+ const keptLines = lines.slice(-MAX_LOG_LINES);
35
+ await writeFile(filePath, keptLines.join("\n") + "\n", "utf-8");
36
+ }
37
+ }
38
+ catch {
39
+ // Silently ignore truncation errors — log remains intact
40
+ }
41
+ }
42
+ //# sourceMappingURL=audit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"audit.js","sourceRoot":"","sources":["../../lib/audit.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,aAAa,GAAG,EAAE,CAAC;AAEzB,MAAM,CAAC,KAAK,UAAU,GAAG,CACvB,YAAoB,EACpB,KAAa,EACb,IAA6B;IAE7B,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC;IACxD,MAAM,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC;QAC3B,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QAC5B,KAAK;QACL,GAAG,IAAI;KACR,CAAC,CAAC;IACH,IAAI,CAAC;QACH,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;QACzC,MAAM,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,kDAAkD;QAClD,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrD,MAAM,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACpD,MAAM,UAAU,CAAC,QAAQ,EAAE,KAAK,GAAG,IAAI,CAAC,CAAC;QAC3C,CAAC;QACD,2EAA2E;IAC7E,CAAC;AACH,CAAC;AAED,KAAK,UAAU,gBAAgB,CAAC,QAAgB;IAC9C,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAEpE,IAAI,KAAK,CAAC,MAAM,GAAG,aAAa,EAAE,CAAC;YACjC,MAAM,SAAS,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,aAAa,CAAC,CAAC;YAC9C,MAAM,SAAS,CAAC,QAAQ,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,EAAE,OAAO,CAAC,CAAC;QAClE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yDAAyD;IAC3D,CAAC;AACH,CAAC"}
@@ -0,0 +1,35 @@
1
+ /**
2
+ * binding-manager.ts — Channel binding analysis and migration.
3
+ *
4
+ * Handles detection of existing channel bindings, channel availability,
5
+ * and safe migration of bindings between agents.
6
+ */
7
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
8
+ export type ChannelType = string;
9
+ export interface BindingAnalysis {
10
+ channelEnabled: boolean;
11
+ channelConfigured: boolean;
12
+ existingChannelWideBinding?: {
13
+ agentId: string;
14
+ agentName: string;
15
+ };
16
+ groupSpecificBindings: Array<{
17
+ agentId: string;
18
+ agentName: string;
19
+ groupId: string;
20
+ }>;
21
+ recommendation: string;
22
+ }
23
+ /**
24
+ * Analyze the current state of channel bindings for a given channel.
25
+ */
26
+ export declare function analyzeChannelBindings(api: OpenClawPluginApi, channel: ChannelType): Promise<BindingAnalysis>;
27
+ /**
28
+ * Migrate a channel-wide binding from one agent to another.
29
+ */
30
+ export declare function migrateChannelBinding(api: OpenClawPluginApi, channel: ChannelType, fromAgentId: string, toAgentId: string): Promise<void>;
31
+ /**
32
+ * Remove a channel-wide binding for a specific agent.
33
+ */
34
+ export declare function removeChannelBinding(api: OpenClawPluginApi, channel: ChannelType, agentId: string): Promise<void>;
35
+ //# sourceMappingURL=binding-manager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-manager.d.ts","sourceRoot":"","sources":["../../lib/binding-manager.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7D,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC;AAEjC,MAAM,WAAW,eAAe;IAC9B,cAAc,EAAE,OAAO,CAAC;IACxB,iBAAiB,EAAE,OAAO,CAAC;IAC3B,0BAA0B,CAAC,EAAE;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,qBAAqB,EAAE,KAAK,CAAC;QAC3B,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,cAAc,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,eAAe,CAAC,CA4D1B;AAED;;GAEG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,WAAW,EACpB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED;;GAEG;AACH,wBAAsB,oBAAoB,CACxC,GAAG,EAAE,iBAAiB,EACtB,OAAO,EAAE,WAAW,EACpB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,IAAI,CAAC,CAUf"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Analyze the current state of channel bindings for a given channel.
3
+ */
4
+ export async function analyzeChannelBindings(api, channel) {
5
+ const config = api.runtime.config.loadConfig();
6
+ // Check if channel is configured and enabled
7
+ const channelConfig = config.channels?.[channel];
8
+ const channelConfigured = !!channelConfig;
9
+ const channelEnabled = channelConfig?.enabled === true;
10
+ // Find existing bindings
11
+ const bindings = config.bindings ?? [];
12
+ let existingChannelWideBinding;
13
+ const groupSpecificBindings = [];
14
+ for (const binding of bindings) {
15
+ if (binding.match?.channel === channel) {
16
+ const agent = config.agents?.list?.find((a) => a.id === binding.agentId);
17
+ const agentName = agent?.name ?? binding.agentId;
18
+ if (!binding.match.peer) {
19
+ // Channel-wide binding (no peer filter) - potential conflict
20
+ existingChannelWideBinding = {
21
+ agentId: binding.agentId,
22
+ agentName,
23
+ };
24
+ }
25
+ else if (binding.match.peer.kind === "group") {
26
+ // Group-specific binding - no conflict
27
+ groupSpecificBindings.push({
28
+ agentId: binding.agentId,
29
+ agentName,
30
+ groupId: binding.match.peer.id,
31
+ });
32
+ }
33
+ }
34
+ }
35
+ // Generate recommendation
36
+ let recommendation;
37
+ if (!channelConfigured) {
38
+ recommendation = `āš ļø ${channel} is not configured in OpenClaw. Configure it first via the wizard or openclaw.json, then restart OpenClaw.`;
39
+ }
40
+ else if (!channelEnabled) {
41
+ recommendation = `āš ļø ${channel} is configured but disabled. Enable it in openclaw.json (channels.${channel}.enabled: true) and restart OpenClaw.`;
42
+ }
43
+ else if (existingChannelWideBinding) {
44
+ recommendation = `āš ļø Agent "${existingChannelWideBinding.agentName}" is already bound to all ${channel} messages. Options:\n 1. Migrate binding to the new agent (recommended if replacing)\n 2. Use group-specific binding instead (if you want both agents active)\n 3. Skip binding for now`;
45
+ }
46
+ else if (groupSpecificBindings.length > 0) {
47
+ recommendation = `āœ… ${groupSpecificBindings.length} group-specific binding(s) exist. No conflicts - safe to add channel-wide binding.`;
48
+ }
49
+ else {
50
+ recommendation = `āœ… No existing ${channel} bindings. Safe to bind the new agent.`;
51
+ }
52
+ return {
53
+ channelEnabled,
54
+ channelConfigured,
55
+ existingChannelWideBinding,
56
+ groupSpecificBindings,
57
+ recommendation,
58
+ };
59
+ }
60
+ /**
61
+ * Migrate a channel-wide binding from one agent to another.
62
+ */
63
+ export async function migrateChannelBinding(api, channel, fromAgentId, toAgentId) {
64
+ const config = api.runtime.config.loadConfig();
65
+ const bindings = config.bindings ?? [];
66
+ // Find the channel-wide binding for this channel and agent
67
+ const bindingIndex = bindings.findIndex((b) => b.match?.channel === channel &&
68
+ !b.match.peer &&
69
+ b.agentId === fromAgentId);
70
+ if (bindingIndex === -1) {
71
+ throw new Error(`No channel-wide ${channel} binding found for agent "${fromAgentId}"`);
72
+ }
73
+ // Update the binding to point to the new agent
74
+ bindings[bindingIndex].agentId = toAgentId;
75
+ config.bindings = bindings;
76
+ await api.runtime.config.writeConfigFile(config);
77
+ }
78
+ /**
79
+ * Remove a channel-wide binding for a specific agent.
80
+ */
81
+ export async function removeChannelBinding(api, channel, agentId) {
82
+ const config = api.runtime.config.loadConfig();
83
+ const bindings = config.bindings ?? [];
84
+ // Filter out the channel-wide binding for this channel and agent
85
+ config.bindings = bindings.filter((b) => !(b.match?.channel === channel && !b.match.peer && b.agentId === agentId));
86
+ await api.runtime.config.writeConfigFile(config);
87
+ }
88
+ //# sourceMappingURL=binding-manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"binding-manager.js","sourceRoot":"","sources":["../../lib/binding-manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * cli.ts — CLI registration for `openclaw devclaw setup` and `openclaw devclaw heartbeat`.
3
+ *
4
+ * Uses Commander.js (provided by OpenClaw plugin SDK context).
5
+ */
6
+ import type { Command } from "commander";
7
+ import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
8
+ /**
9
+ * Register the `devclaw` CLI command group on a Commander program.
10
+ */
11
+ export declare function registerCli(program: Command, api: OpenClawPluginApi): void;
12
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAI7D;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,GAAG,EAAE,iBAAiB,GAAG,IAAI,CA+D1E"}
@@ -0,0 +1,69 @@
1
+ import { runSetup } from "./setup/index.js";
2
+ import { DEV_LEVELS, QA_LEVELS, DEFAULT_MODELS } from "./tiers.js";
3
+ /**
4
+ * Register the `devclaw` CLI command group on a Commander program.
5
+ */
6
+ export function registerCli(program, api) {
7
+ const devclaw = program
8
+ .command("devclaw")
9
+ .description("DevClaw development pipeline tools");
10
+ devclaw
11
+ .command("setup")
12
+ .description("Set up DevClaw: create agent, configure models, write workspace files")
13
+ .option("--new-agent <name>", "Create a new agent with this name")
14
+ .option("--agent <id>", "Use an existing agent by ID")
15
+ .option("--workspace <path>", "Direct workspace path")
16
+ .option("--junior <model>", `Junior dev model (default: ${DEFAULT_MODELS.dev.junior})`)
17
+ .option("--medior <model>", `Medior dev model (default: ${DEFAULT_MODELS.dev.medior})`)
18
+ .option("--senior <model>", `Senior dev model (default: ${DEFAULT_MODELS.dev.senior})`)
19
+ .option("--reviewer <model>", `Reviewer model (default: ${DEFAULT_MODELS.qa.reviewer})`)
20
+ .option("--tester <model>", `Tester model (default: ${DEFAULT_MODELS.qa.tester})`)
21
+ .action(async (opts) => {
22
+ const dev = {};
23
+ const qa = {};
24
+ if (opts.junior)
25
+ dev.junior = opts.junior;
26
+ if (opts.medior)
27
+ dev.medior = opts.medior;
28
+ if (opts.senior)
29
+ dev.senior = opts.senior;
30
+ if (opts.reviewer)
31
+ qa.reviewer = opts.reviewer;
32
+ if (opts.tester)
33
+ qa.tester = opts.tester;
34
+ const hasOverrides = Object.keys(dev).length > 0 || Object.keys(qa).length > 0;
35
+ const models = hasOverrides
36
+ ? { ...(Object.keys(dev).length > 0 && { dev }), ...(Object.keys(qa).length > 0 && { qa }) }
37
+ : undefined;
38
+ const result = await runSetup({
39
+ api,
40
+ newAgentName: opts.newAgent,
41
+ agentId: opts.agent,
42
+ workspacePath: opts.workspace,
43
+ models,
44
+ });
45
+ if (result.agentCreated) {
46
+ console.log(`Agent "${result.agentId}" created`);
47
+ }
48
+ console.log("Models configured:");
49
+ for (const t of DEV_LEVELS)
50
+ console.log(` dev.${t}: ${result.models.dev[t]}`);
51
+ for (const t of QA_LEVELS)
52
+ console.log(` qa.${t}: ${result.models.qa[t]}`);
53
+ console.log("Files written:");
54
+ for (const file of result.filesWritten) {
55
+ console.log(` ${file}`);
56
+ }
57
+ if (result.warnings.length > 0) {
58
+ console.log("\nWarnings:");
59
+ for (const w of result.warnings) {
60
+ console.log(` ${w}`);
61
+ }
62
+ }
63
+ console.log("\nDone! Next steps:");
64
+ console.log(" 1. Add bot to a Telegram group");
65
+ console.log(' 2. Register a project: "Register project <name> at <repo> for group <id>"');
66
+ console.log(" 3. Create your first issue and pick it up");
67
+ });
68
+ }
69
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../../lib/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,58 @@
1
+ import { type Project } from "./projects.js";
2
+ export type DispatchOpts = {
3
+ workspaceDir: string;
4
+ agentId?: string;
5
+ groupId: string;
6
+ project: Project;
7
+ issueId: number;
8
+ issueTitle: string;
9
+ issueDescription: string;
10
+ issueUrl: string;
11
+ role: "dev" | "qa";
12
+ /** Developer level (junior, medior, senior, reviewer) or raw model ID */
13
+ level: string;
14
+ /** Label to transition FROM (e.g. "To Do", "To Test", "To Improve") */
15
+ fromLabel: string;
16
+ /** Label to transition TO (e.g. "Doing", "Testing") */
17
+ toLabel: string;
18
+ /** Function to transition labels (injected to avoid provider dependency) */
19
+ transitionLabel: (issueId: number, from: string, to: string) => Promise<void>;
20
+ /** Plugin config for model resolution */
21
+ pluginConfig?: Record<string, unknown>;
22
+ /** Orchestrator's session key (used as spawnedBy for subagent tracking) */
23
+ sessionKey?: string;
24
+ };
25
+ export type DispatchResult = {
26
+ sessionAction: "spawn" | "send";
27
+ sessionKey: string;
28
+ level: string;
29
+ model: string;
30
+ announcement: string;
31
+ };
32
+ /**
33
+ * Build the task message sent to a worker session.
34
+ * Reads role-specific instructions from workspace/projects/roles/<project>/<role>.md (falls back to projects/roles/default/).
35
+ */
36
+ export declare function buildTaskMessage(opts: {
37
+ workspaceDir: string;
38
+ projectName: string;
39
+ role: "dev" | "qa";
40
+ issueId: number;
41
+ issueTitle: string;
42
+ issueDescription: string;
43
+ issueUrl: string;
44
+ repo: string;
45
+ baseBranch: string;
46
+ groupId: string;
47
+ }): Promise<string>;
48
+ /**
49
+ * Dispatch a task to a worker session.
50
+ *
51
+ * Flow: resolve model → build message → transition label → spawn/send session
52
+ * → update worker state → audit → build announcement.
53
+ *
54
+ * On dispatch failure: rolls back label transition.
55
+ * On state update failure after dispatch: logs warning (session IS running).
56
+ */
57
+ export declare function dispatchTask(opts: DispatchOpts): Promise<DispatchResult>;
58
+ //# sourceMappingURL=dispatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../lib/dispatch.ts"],"names":[],"mappings":"AAWA,OAAO,EACL,KAAK,OAAO,EAIb,MAAM,eAAe,CAAC;AAKvB,MAAM,MAAM,YAAY,GAAG;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,yEAAyE;IACzE,KAAK,EAAE,MAAM,CAAC;IACd,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;IAClB,uDAAuD;IACvD,OAAO,EAAE,MAAM,CAAC;IAChB,4EAA4E;IAC5E,eAAe,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9E,yCAAyC;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACvC,2EAA2E;IAC3E,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,aAAa,EAAE,OAAO,GAAG,MAAM,CAAC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEF;;;GAGG;AACH,wBAAsB,gBAAgB,CAAC,IAAI,EAAE;IAC3C,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,KAAK,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,MAAM,CAAC;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,MAAM,CAAC,CA2ClB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,IAAI,EAAE,YAAY,GACjB,OAAO,CAAC,cAAc,CAAC,CAgEzB"}
@@ -0,0 +1,163 @@
1
+ /**
2
+ * dispatch.ts — Core dispatch logic shared by work_start and projectTick.
3
+ *
4
+ * Handles: session lookup, spawn/reuse via Gateway RPC, task dispatch via CLI,
5
+ * state update (activateWorker), and audit logging.
6
+ */
7
+ import { execFile, spawn } from "node:child_process";
8
+ import fs from "node:fs/promises";
9
+ import path from "node:path";
10
+ import { promisify } from "node:util";
11
+ import { log as auditLog } from "./audit.js";
12
+ import { activateWorker, getSessionForLevel, getWorker, } from "./projects.js";
13
+ import { resolveModel, levelEmoji } from "./tiers.js";
14
+ const execFileAsync = promisify(execFile);
15
+ /**
16
+ * Build the task message sent to a worker session.
17
+ * Reads role-specific instructions from workspace/projects/roles/<project>/<role>.md (falls back to projects/roles/default/).
18
+ */
19
+ export async function buildTaskMessage(opts) {
20
+ const { workspaceDir, projectName, role, issueId, issueTitle, issueDescription, issueUrl, repo, baseBranch, groupId, } = opts;
21
+ const roleInstructions = await loadRoleInstructions(workspaceDir, projectName, role);
22
+ const availableResults = role === "dev"
23
+ ? '"done" (completed successfully) or "blocked" (cannot complete, need help)'
24
+ : '"pass" (approved), "fail" (issues found), "refine" (needs human input), or "blocked" (cannot complete)';
25
+ const parts = [
26
+ `${role.toUpperCase()} task for project "${projectName}" — Issue #${issueId}`,
27
+ ``,
28
+ issueTitle,
29
+ issueDescription ? `\n${issueDescription}` : "",
30
+ ``,
31
+ `Repo: ${repo} | Branch: ${baseBranch} | ${issueUrl}`,
32
+ `Project group ID: ${groupId}`,
33
+ ];
34
+ if (roleInstructions) {
35
+ parts.push(``, `---`, ``, roleInstructions.trim());
36
+ }
37
+ parts.push(``, `---`, ``, `## MANDATORY: Task Completion`, ``, `When you finish this task, you MUST call \`work_finish\` with:`, `- \`role\`: "${role}"`, `- \`projectGroupId\`: "${groupId}"`, `- \`result\`: ${availableResults}`, `- \`summary\`: brief description of what you did`, ``, `āš ļø You MUST call work_finish even if you encounter errors or cannot finish.`, `Use "blocked" with a summary explaining why you're stuck.`, `Never end your session without calling work_finish.`);
38
+ return parts.join("\n");
39
+ }
40
+ /**
41
+ * Dispatch a task to a worker session.
42
+ *
43
+ * Flow: resolve model → build message → transition label → spawn/send session
44
+ * → update worker state → audit → build announcement.
45
+ *
46
+ * On dispatch failure: rolls back label transition.
47
+ * On state update failure after dispatch: logs warning (session IS running).
48
+ */
49
+ export async function dispatchTask(opts) {
50
+ const { workspaceDir, agentId, groupId, project, issueId, issueTitle, issueDescription, issueUrl, role, level, fromLabel, toLabel, transitionLabel, pluginConfig, } = opts;
51
+ const model = resolveModel(role, level, pluginConfig);
52
+ const worker = getWorker(project, role);
53
+ const existingSessionKey = getSessionForLevel(worker, level);
54
+ const sessionAction = existingSessionKey ? "send" : "spawn";
55
+ const taskMessage = await buildTaskMessage({
56
+ workspaceDir, projectName: project.name, role, issueId,
57
+ issueTitle, issueDescription, issueUrl,
58
+ repo: project.repo, baseBranch: project.baseBranch, groupId,
59
+ });
60
+ await transitionLabel(issueId, fromLabel, toLabel);
61
+ let sessionKey = existingSessionKey;
62
+ let dispatched = false;
63
+ try {
64
+ sessionKey = await ensureSession(sessionAction, sessionKey, {
65
+ agentId, projectName: project.name, role, level, model,
66
+ });
67
+ await sendToAgent(sessionKey, taskMessage, {
68
+ agentId, projectName: project.name, issueId, role,
69
+ orchestratorSessionKey: opts.sessionKey,
70
+ });
71
+ dispatched = true;
72
+ await recordWorkerState(workspaceDir, groupId, role, {
73
+ issueId, level, sessionKey: sessionKey, sessionAction,
74
+ });
75
+ }
76
+ catch (err) {
77
+ if (dispatched) {
78
+ await auditLog(workspaceDir, "work_start", {
79
+ project: project.name, groupId, issue: issueId, role,
80
+ warning: "State update failed after successful dispatch",
81
+ error: err.message, sessionKey,
82
+ });
83
+ throw new Error(`State update failed after successful session dispatch: ${err.message}. Session is running but projects.json was not updated.`);
84
+ }
85
+ try {
86
+ await transitionLabel(issueId, toLabel, fromLabel);
87
+ }
88
+ catch { /* best-effort rollback */ }
89
+ throw new Error(`Session dispatch failed: ${err.message}. Label reverted to "${fromLabel}".`);
90
+ }
91
+ await auditDispatch(workspaceDir, {
92
+ project: project.name, groupId, issueId, issueTitle,
93
+ role, level, model, sessionAction, sessionKey: sessionKey,
94
+ fromLabel, toLabel,
95
+ });
96
+ const announcement = buildAnnouncement(level, role, sessionAction, issueId, issueTitle, issueUrl);
97
+ return { sessionAction, sessionKey: sessionKey, level, model, announcement };
98
+ }
99
+ // ---------------------------------------------------------------------------
100
+ // Private helpers — exist so dispatchTask reads as a sequence of steps
101
+ // ---------------------------------------------------------------------------
102
+ async function loadRoleInstructions(workspaceDir, projectName, role) {
103
+ const projectFile = path.join(workspaceDir, "projects", "roles", projectName, `${role}.md`);
104
+ try {
105
+ return await fs.readFile(projectFile, "utf-8");
106
+ }
107
+ catch { /* none */ }
108
+ const defaultFile = path.join(workspaceDir, "projects", "roles", "default", `${role}.md`);
109
+ try {
110
+ return await fs.readFile(defaultFile, "utf-8");
111
+ }
112
+ catch { /* none */ }
113
+ return "";
114
+ }
115
+ async function ensureSession(action, existingKey, opts) {
116
+ if (action === "send")
117
+ return existingKey;
118
+ const sessionKey = `agent:${opts.agentId ?? "unknown"}:subagent:${opts.projectName}-${opts.role}-${opts.level}`;
119
+ await execFileAsync("openclaw", ["gateway", "call", "sessions.patch", "--params", JSON.stringify({ key: sessionKey, model: opts.model })], { timeout: 30_000 });
120
+ return sessionKey;
121
+ }
122
+ function sendToAgent(sessionKey, taskMessage, opts) {
123
+ const gatewayParams = JSON.stringify({
124
+ idempotencyKey: `devclaw-${opts.projectName}-${opts.issueId}-${opts.role}-${Date.now()}`,
125
+ agentId: opts.agentId ?? "devclaw",
126
+ sessionKey,
127
+ message: taskMessage,
128
+ deliver: false,
129
+ lane: "subagent",
130
+ ...(opts.orchestratorSessionKey ? { spawnedBy: opts.orchestratorSessionKey } : {}),
131
+ });
132
+ const child = spawn("openclaw", ["gateway", "call", "agent", "--params", gatewayParams, "--expect-final", "--json"], { detached: true, stdio: "ignore" });
133
+ child.unref();
134
+ }
135
+ async function recordWorkerState(workspaceDir, groupId, role, opts) {
136
+ const params = {
137
+ issueId: String(opts.issueId),
138
+ level: opts.level,
139
+ startTime: new Date().toISOString(),
140
+ };
141
+ if (opts.sessionAction === "spawn") {
142
+ params.sessionKey = opts.sessionKey;
143
+ }
144
+ await activateWorker(workspaceDir, groupId, role, params);
145
+ }
146
+ async function auditDispatch(workspaceDir, opts) {
147
+ await auditLog(workspaceDir, "work_start", {
148
+ project: opts.project, groupId: opts.groupId,
149
+ issue: opts.issueId, issueTitle: opts.issueTitle,
150
+ role: opts.role, level: opts.level,
151
+ sessionAction: opts.sessionAction, sessionKey: opts.sessionKey,
152
+ labelTransition: `${opts.fromLabel} → ${opts.toLabel}`,
153
+ });
154
+ await auditLog(workspaceDir, "model_selection", {
155
+ issue: opts.issueId, role: opts.role, level: opts.level, model: opts.model,
156
+ });
157
+ }
158
+ function buildAnnouncement(level, role, sessionAction, issueId, issueTitle, issueUrl) {
159
+ const emoji = levelEmoji(role, level) ?? (role === "qa" ? "šŸ”" : "šŸ”§");
160
+ const actionVerb = sessionAction === "spawn" ? "Spawning" : "Sending";
161
+ return `${emoji} ${actionVerb} ${role.toUpperCase()} (${level}) for #${issueId}: ${issueTitle}\nšŸ”— ${issueUrl}`;
162
+ }
163
+ //# sourceMappingURL=dispatch.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"dispatch.js","sourceRoot":"","sources":["../../lib/dispatch.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Model selection for dev/qa tasks.
3
+ * Keyword heuristic fallback — used when the orchestrator doesn't specify a level.
4
+ * Returns plain level names (junior, medior, senior, reviewer, tester).
5
+ */
6
+ export type LevelSelection = {
7
+ level: string;
8
+ reason: string;
9
+ };
10
+ /**
11
+ * Select appropriate developer level based on task description.
12
+ *
13
+ * Developer levels:
14
+ * - junior: very simple (typos, single-file fixes, CSS tweaks)
15
+ * - medior: standard DEV (features, bug fixes, multi-file changes)
16
+ * - senior: deep/architectural (system-wide refactoring, novel design)
17
+ * - reviewer: QA code inspection and validation
18
+ * - tester: QA manual testing
19
+ */
20
+ export declare function selectLevel(issueTitle: string, issueDescription: string, role: "dev" | "qa"): LevelSelection;
21
+ //# sourceMappingURL=model-selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selector.d.ts","sourceRoot":"","sources":["../../lib/model-selector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AA+BF;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CACzB,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,EACxB,IAAI,EAAE,KAAK,GAAG,IAAI,GACjB,cAAc,CAkChB"}
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Model selection for dev/qa tasks.
3
+ * Keyword heuristic fallback — used when the orchestrator doesn't specify a level.
4
+ * Returns plain level names (junior, medior, senior, reviewer, tester).
5
+ */
6
+ // Keywords that indicate simple tasks
7
+ const SIMPLE_KEYWORDS = [
8
+ "typo",
9
+ "fix typo",
10
+ "rename",
11
+ "update text",
12
+ "change color",
13
+ "minor",
14
+ "small",
15
+ "css",
16
+ "style",
17
+ "copy",
18
+ "wording",
19
+ ];
20
+ // Keywords that indicate complex tasks
21
+ const COMPLEX_KEYWORDS = [
22
+ "architect",
23
+ "refactor",
24
+ "redesign",
25
+ "system-wide",
26
+ "migration",
27
+ "database schema",
28
+ "security",
29
+ "performance",
30
+ "infrastructure",
31
+ "multi-service",
32
+ ];
33
+ /**
34
+ * Select appropriate developer level based on task description.
35
+ *
36
+ * Developer levels:
37
+ * - junior: very simple (typos, single-file fixes, CSS tweaks)
38
+ * - medior: standard DEV (features, bug fixes, multi-file changes)
39
+ * - senior: deep/architectural (system-wide refactoring, novel design)
40
+ * - reviewer: QA code inspection and validation
41
+ * - tester: QA manual testing
42
+ */
43
+ export function selectLevel(issueTitle, issueDescription, role) {
44
+ if (role === "qa") {
45
+ return {
46
+ level: "reviewer",
47
+ reason: "Default QA level for code inspection and validation",
48
+ };
49
+ }
50
+ const text = `${issueTitle} ${issueDescription}`.toLowerCase();
51
+ const wordCount = text.split(/\s+/).length;
52
+ // Check for simple task indicators
53
+ const isSimple = SIMPLE_KEYWORDS.some((kw) => text.includes(kw));
54
+ if (isSimple && wordCount < 100) {
55
+ return {
56
+ level: "junior",
57
+ reason: `Simple task detected (keywords: ${SIMPLE_KEYWORDS.filter((kw) => text.includes(kw)).join(", ")})`,
58
+ };
59
+ }
60
+ // Check for complex task indicators
61
+ const isComplex = COMPLEX_KEYWORDS.some((kw) => text.includes(kw));
62
+ if (isComplex || wordCount > 500) {
63
+ return {
64
+ level: "senior",
65
+ reason: `Complex task detected (${isComplex ? "keywords: " + COMPLEX_KEYWORDS.filter((kw) => text.includes(kw)).join(", ") : "long description"})`,
66
+ };
67
+ }
68
+ // Default: medior for standard dev work
69
+ return {
70
+ level: "medior",
71
+ reason: "Standard dev task — multi-file changes, features, bug fixes",
72
+ };
73
+ }
74
+ //# sourceMappingURL=model-selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"model-selector.js","sourceRoot":"","sources":["../../lib/model-selector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,sCAAsC;AACtC,MAAM,eAAe,GAAG;IACtB,MAAM;IACN,UAAU;IACV,QAAQ;IACR,aAAa;IACb,cAAc;IACd,OAAO;IACP,OAAO;IACP,KAAK;IACL,OAAO;IACP,MAAM;IACN,SAAS;CACV,CAAC;AAEF,uCAAuC;AACvC,MAAM,gBAAgB,GAAG;IACvB,WAAW;IACX,UAAU;IACV,UAAU;IACV,aAAa;IACb,WAAW;IACX,iBAAiB;IACjB,UAAU;IACV,aAAa;IACb,gBAAgB;IAChB,eAAe;CAChB,CAAC;AAEF;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,gBAAwB,EACxB,IAAkB;IAElB,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAClB,OAAO;YACL,KAAK,EAAE,UAAU;YACjB,MAAM,EAAE,qDAAqD;SAC9D,CAAC;IACJ,CAAC;IAED,MAAM,IAAI,GAAG,GAAG,UAAU,IAAI,gBAAgB,EAAE,CAAC,WAAW,EAAE,CAAC;IAC/D,MAAM,SAAS,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC;IAE3C,mCAAmC;IACnC,MAAM,QAAQ,GAAG,eAAe,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACjE,IAAI,QAAQ,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QAChC,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,mCAAmC,eAAe,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG;SAC3G,CAAC;IACJ,CAAC;IAED,oCAAoC;IACpC,MAAM,SAAS,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IACnE,IAAI,SAAS,IAAI,SAAS,GAAG,GAAG,EAAE,CAAC;QACjC,OAAO;YACL,KAAK,EAAE,QAAQ;YACf,MAAM,EAAE,0BAA0B,SAAS,CAAC,CAAC,CAAC,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,kBAAkB,GAAG;SACnJ,CAAC;IACJ,CAAC;IAED,wCAAwC;IACxC,OAAO;QACL,KAAK,EAAE,QAAQ;QACf,MAAM,EAAE,6DAA6D;KACtE,CAAC;AACJ,CAAC"}