locus-product-planning 1.0.0 → 1.2.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 (76) hide show
  1. package/.claude-plugin/marketplace.json +31 -0
  2. package/.claude-plugin/plugin.json +32 -0
  3. package/README.md +131 -45
  4. package/agents/engineering/architect-reviewer.md +122 -0
  5. package/agents/engineering/engineering-manager.md +101 -0
  6. package/agents/engineering/principal-engineer.md +98 -0
  7. package/agents/engineering/staff-engineer.md +86 -0
  8. package/agents/engineering/tech-lead.md +114 -0
  9. package/agents/executive/ceo-strategist.md +81 -0
  10. package/agents/executive/cfo-analyst.md +97 -0
  11. package/agents/executive/coo-operations.md +100 -0
  12. package/agents/executive/cpo-product.md +104 -0
  13. package/agents/executive/cto-architect.md +90 -0
  14. package/agents/product/product-manager.md +70 -0
  15. package/agents/product/project-manager.md +95 -0
  16. package/agents/product/qa-strategist.md +132 -0
  17. package/agents/product/scrum-master.md +70 -0
  18. package/dist/index.d.ts +10 -25
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +231 -95
  21. package/dist/lib/skills-core.d.ts +95 -0
  22. package/dist/lib/skills-core.d.ts.map +1 -0
  23. package/dist/lib/skills-core.js +361 -0
  24. package/hooks/hooks.json +15 -0
  25. package/hooks/run-hook.cmd +32 -0
  26. package/hooks/session-start.cmd +13 -0
  27. package/hooks/session-start.sh +70 -0
  28. package/opencode.json +11 -7
  29. package/package.json +18 -4
  30. package/skills/01-executive-suite/ceo-strategist/SKILL.md +132 -0
  31. package/skills/01-executive-suite/cfo-analyst/SKILL.md +187 -0
  32. package/skills/01-executive-suite/coo-operations/SKILL.md +211 -0
  33. package/skills/01-executive-suite/cpo-product/SKILL.md +231 -0
  34. package/skills/01-executive-suite/cto-architect/SKILL.md +173 -0
  35. package/skills/02-product-management/estimation-expert/SKILL.md +139 -0
  36. package/skills/02-product-management/product-manager/SKILL.md +265 -0
  37. package/skills/02-product-management/program-manager/SKILL.md +178 -0
  38. package/skills/02-product-management/project-manager/SKILL.md +221 -0
  39. package/skills/02-product-management/roadmap-strategist/SKILL.md +186 -0
  40. package/skills/02-product-management/scrum-master/SKILL.md +212 -0
  41. package/skills/03-engineering-leadership/architect-reviewer/SKILL.md +249 -0
  42. package/skills/03-engineering-leadership/engineering-manager/SKILL.md +207 -0
  43. package/skills/03-engineering-leadership/principal-engineer/SKILL.md +206 -0
  44. package/skills/03-engineering-leadership/staff-engineer/SKILL.md +237 -0
  45. package/skills/03-engineering-leadership/tech-lead/SKILL.md +296 -0
  46. package/skills/04-developer-specializations/core/api-designer/SKILL.md +579 -0
  47. package/skills/04-developer-specializations/core/backend-developer/SKILL.md +205 -0
  48. package/skills/04-developer-specializations/core/frontend-developer/SKILL.md +233 -0
  49. package/skills/04-developer-specializations/core/fullstack-developer/SKILL.md +202 -0
  50. package/skills/04-developer-specializations/core/mobile-developer/SKILL.md +220 -0
  51. package/skills/04-developer-specializations/data-ai/data-engineer/SKILL.md +316 -0
  52. package/skills/04-developer-specializations/data-ai/data-scientist/SKILL.md +338 -0
  53. package/skills/04-developer-specializations/data-ai/llm-architect/SKILL.md +390 -0
  54. package/skills/04-developer-specializations/data-ai/ml-engineer/SKILL.md +349 -0
  55. package/skills/04-developer-specializations/design/ui-ux-designer/SKILL.md +337 -0
  56. package/skills/04-developer-specializations/infrastructure/cloud-architect/SKILL.md +354 -0
  57. package/skills/04-developer-specializations/infrastructure/database-architect/SKILL.md +430 -0
  58. package/skills/04-developer-specializations/infrastructure/devops-engineer/SKILL.md +306 -0
  59. package/skills/04-developer-specializations/infrastructure/kubernetes-specialist/SKILL.md +419 -0
  60. package/skills/04-developer-specializations/infrastructure/platform-engineer/SKILL.md +289 -0
  61. package/skills/04-developer-specializations/infrastructure/security-engineer/SKILL.md +336 -0
  62. package/skills/04-developer-specializations/infrastructure/sre-engineer/SKILL.md +425 -0
  63. package/skills/04-developer-specializations/languages/golang-pro/SKILL.md +366 -0
  64. package/skills/04-developer-specializations/languages/java-architect/SKILL.md +296 -0
  65. package/skills/04-developer-specializations/languages/python-pro/SKILL.md +317 -0
  66. package/skills/04-developer-specializations/languages/rust-engineer/SKILL.md +309 -0
  67. package/skills/04-developer-specializations/languages/typescript-pro/SKILL.md +251 -0
  68. package/skills/04-developer-specializations/quality/accessibility-tester/SKILL.md +338 -0
  69. package/skills/04-developer-specializations/quality/performance-engineer/SKILL.md +384 -0
  70. package/skills/04-developer-specializations/quality/qa-expert/SKILL.md +413 -0
  71. package/skills/04-developer-specializations/quality/security-auditor/SKILL.md +359 -0
  72. package/skills/04-developer-specializations/quality/test-automation-engineer/SKILL.md +711 -0
  73. package/skills/05-specialists/compliance-specialist/SKILL.md +171 -0
  74. package/skills/05-specialists/technical-writer/SKILL.md +576 -0
  75. package/skills/using-locus/SKILL.md +126 -0
  76. package/.opencode/skills/locus/SKILL.md +0 -299
package/dist/index.js CHANGED
@@ -1,114 +1,250 @@
1
1
  /**
2
2
  * Locus - OpenCode Plugin
3
3
  *
4
- * AI-powered project planning: Vision Features → Design → Build
5
- *
6
- * This plugin provides the /locus commands for project planning.
7
- * The main skill is auto-discovered from .opencode/skills/locus/SKILL.md
4
+ * AI-powered project planning with skills framework.
5
+ * Provides custom tools for loading and discovering skills,
6
+ * with automatic bootstrap on session start.
8
7
  */
9
- import { readFileSync, existsSync, readdirSync, statSync } from "fs";
10
- import { join, dirname } from "path";
8
+ import { tool } from "@opencode-ai/plugin/tool";
9
+ import { readFileSync } from "fs";
10
+ import { join, dirname, resolve } from "path";
11
11
  import { fileURLToPath } from "url";
12
+ import { homedir } from "os";
13
+ import { findSkillsInDir, findAgentsInDir, resolveSkillPath, extractFrontmatter, stripFrontmatter, getBootstrapContent, normalizePath, } from "./lib/skills-core.js";
12
14
  const __filename = fileURLToPath(import.meta.url);
13
15
  const __dirname = dirname(__filename);
14
- // Path to skill file (relative to dist/)
15
- const SKILL_PATH = join(__dirname, "..", ".opencode", "skills", "locus", "SKILL.md");
16
- /**
17
- * Read the Locus skill content
18
- */
19
- function getSkillContent() {
20
- try {
21
- return readFileSync(SKILL_PATH, "utf-8");
22
- }
23
- catch {
24
- return "Locus skill not found. Please reinstall opencode-locus.";
25
- }
26
- }
27
16
  /**
28
- * Get project state from .locus-state.yaml
17
+ * Locus Plugin for OpenCode
18
+ *
19
+ * Provides:
20
+ * - use_skill: Load and read a specific skill
21
+ * - find_skills: List all available skills
22
+ * - find_agents: List all available agents
23
+ * - Bootstrap injection on session start
29
24
  */
30
- function getProjectState(projectPath) {
31
- const statePath = join(projectPath, ".locus-state.yaml");
32
- if (!existsSync(statePath))
33
- return null;
34
- try {
35
- const content = readFileSync(statePath, "utf-8");
36
- // Simple YAML parsing for our state format
37
- const state = {};
38
- const lines = content.split("\n");
39
- for (const line of lines) {
40
- const match = line.match(/^(\w+):\s*(.+)$/);
41
- if (match) {
42
- state[match[1]] = match[2];
43
- }
25
+ export const LocusPlugin = async ({ client, directory }) => {
26
+ const homeDir = homedir();
27
+ // Skill directories with priority: project > personal > locus
28
+ const projectSkillsDir = join(directory, '.opencode/skills');
29
+ // Derive locus skills dir from plugin location
30
+ const locusSkillsDir = resolve(__dirname, '..', 'skills');
31
+ // Agents directory
32
+ const locusAgentsDir = resolve(__dirname, '..', 'agents');
33
+ const projectAgentsDir = join(directory, 'agents');
34
+ // Personal skills directory (respect OPENCODE_CONFIG_DIR if set)
35
+ const envConfigDir = normalizePath(process.env.OPENCODE_CONFIG_DIR, homeDir);
36
+ const configDir = envConfigDir || join(homeDir, '.config/opencode');
37
+ const personalSkillsDir = join(configDir, 'skills');
38
+ /**
39
+ * Generate bootstrap content for session injection
40
+ */
41
+ const generateBootstrap = (compact = false) => {
42
+ return getBootstrapContent(locusSkillsDir, compact);
43
+ };
44
+ /**
45
+ * Inject bootstrap via session.prompt
46
+ */
47
+ const injectBootstrap = async (sessionID, compact = false) => {
48
+ const bootstrapContent = generateBootstrap(compact);
49
+ if (!bootstrapContent)
50
+ return false;
51
+ try {
52
+ await client.session.prompt({
53
+ path: { id: sessionID },
54
+ body: {
55
+ noReply: true,
56
+ parts: [{ type: "text", text: bootstrapContent, synthetic: true }]
57
+ }
58
+ });
59
+ return true;
44
60
  }
45
- return state;
46
- }
47
- catch {
48
- return null;
49
- }
50
- }
51
- /**
52
- * List all projects in the projects/ directory
53
- */
54
- function listProjects(directory) {
55
- const projectsDir = join(directory, "projects");
56
- if (!existsSync(projectsDir))
57
- return [];
58
- const projects = [];
59
- try {
60
- const entries = readdirSync(projectsDir);
61
- for (const entry of entries) {
62
- const entryPath = join(projectsDir, entry);
63
- const stat = statSync(entryPath);
64
- if (stat.isDirectory() && !entry.startsWith(".")) {
65
- projects.push({
66
- name: entry,
67
- path: entryPath,
68
- state: getProjectState(entryPath),
69
- });
70
- }
61
+ catch {
62
+ return false;
71
63
  }
72
- }
73
- catch {
74
- // Ignore errors
75
- }
76
- return projects;
77
- }
78
- /**
79
- * Format project status for display
80
- */
81
- function formatProjectStatus(state, projectName) {
82
- const steps = ["Vision", "Features", "Design", "Build"];
83
- if (!state) {
84
- return `📋 ${projectName}\n━━━━━━━━━━━━━━━━━━\nNo state found. Start with /locus.`;
85
- }
86
- const currentStep = Number(state.current_step) || 1;
87
- let output = `📋 ${state.project || projectName}\n━━━━━━━━━━━━━━━━━━\n`;
88
- for (let i = 0; i < steps.length; i++) {
89
- const stepNum = i + 1;
90
- const prefix = stepNum < currentStep ? "✓" : stepNum === currentStep ? "→" : " ";
91
- const suffix = stepNum === currentStep ? " ◄ you are here" : "";
92
- output += `${prefix} Step ${stepNum}: ${steps[i]}${suffix}\n`;
93
- }
94
- const progress = Math.round(((currentStep - 1) / 4) * 100);
95
- output += `\nProgress: ${progress}% complete`;
96
- return output;
97
- }
98
- /**
99
- * Locus Plugin for OpenCode
100
- */
101
- export const LocusPlugin = async ({ directory }) => {
64
+ };
102
65
  return {
103
- // Log when plugin loads
66
+ tool: {
67
+ /**
68
+ * Load and read a specific skill to guide work
69
+ */
70
+ use_skill: tool({
71
+ description: 'Load and read a specific skill to guide your work. Skills contain proven workflows, mandatory processes, and expert techniques for project planning and development.',
72
+ args: {
73
+ skill_name: tool.schema.string().describe('Name of the skill to load (e.g., "locus:product-manager", "project:my-skill", or "brainstorming")')
74
+ },
75
+ execute: async (args, context) => {
76
+ const { skill_name } = args;
77
+ // Resolve skill with priority: project > personal > locus
78
+ const resolved = resolveSkillPath(skill_name, locusSkillsDir, personalSkillsDir, projectSkillsDir);
79
+ if (!resolved) {
80
+ return `Error: Skill "${skill_name}" not found.\n\nRun find_skills to see available skills.`;
81
+ }
82
+ const fullContent = readFileSync(resolved.skillFile, 'utf-8');
83
+ const { name, description } = extractFrontmatter(resolved.skillFile);
84
+ const content = stripFrontmatter(fullContent);
85
+ const skillDirectory = dirname(resolved.skillFile);
86
+ const skillHeader = `# ${name || skill_name}
87
+ # ${description || ''}
88
+ # Supporting tools and docs are in ${skillDirectory}
89
+ # ============================================`;
90
+ // Insert as user message with noReply for persistence across compaction
91
+ try {
92
+ await client.session.prompt({
93
+ path: { id: context.sessionID },
94
+ body: {
95
+ agent: context.agent,
96
+ noReply: true,
97
+ parts: [
98
+ { type: "text", text: `Loading skill: ${name || skill_name}`, synthetic: true },
99
+ { type: "text", text: `${skillHeader}\n\n${content}`, synthetic: true }
100
+ ]
101
+ }
102
+ });
103
+ }
104
+ catch {
105
+ // Fallback: return content directly if message insertion fails
106
+ return `${skillHeader}\n\n${content}`;
107
+ }
108
+ return `Launching skill: ${name || skill_name}`;
109
+ }
110
+ }),
111
+ /**
112
+ * List all available skills with optional filtering
113
+ */
114
+ find_skills: tool({
115
+ description: 'List all available skills. Filter by category (e.g., "core", "infrastructure"), tier (e.g., "executive", "developer-specialization"), or search term.',
116
+ args: {
117
+ category: tool.schema.string().optional().describe('Filter by category (e.g., "core", "infrastructure", "data-ai", "quality", "languages")'),
118
+ tier: tool.schema.string().optional().describe('Filter by tier (e.g., "executive", "product-management", "engineering-leadership", "developer-specialization")'),
119
+ search: tool.schema.string().optional().describe('Search term to filter skills by name or description'),
120
+ },
121
+ execute: async (args) => {
122
+ const { category, tier, search } = args;
123
+ const projectSkills = findSkillsInDir(projectSkillsDir, 'project', 4);
124
+ const personalSkills = findSkillsInDir(personalSkillsDir, 'personal', 4);
125
+ const locusSkills = findSkillsInDir(locusSkillsDir, 'locus', 4);
126
+ let allSkills = [...projectSkills, ...personalSkills, ...locusSkills];
127
+ // Apply filters
128
+ if (category) {
129
+ const categoryLower = category.toLowerCase();
130
+ allSkills = allSkills.filter(s => s.category?.toLowerCase().includes(categoryLower) ||
131
+ s.path.toLowerCase().includes(categoryLower));
132
+ }
133
+ if (tier) {
134
+ const tierLower = tier.toLowerCase();
135
+ allSkills = allSkills.filter(s => s.tier?.toLowerCase().includes(tierLower) ||
136
+ s.path.toLowerCase().includes(tierLower));
137
+ }
138
+ if (search) {
139
+ const searchLower = search.toLowerCase();
140
+ allSkills = allSkills.filter(s => s.name.toLowerCase().includes(searchLower) ||
141
+ s.description.toLowerCase().includes(searchLower));
142
+ }
143
+ if (allSkills.length === 0) {
144
+ const filterDesc = [category && `category="${category}"`, tier && `tier="${tier}"`, search && `search="${search}"`]
145
+ .filter(Boolean).join(', ');
146
+ return filterDesc
147
+ ? `No skills found matching: ${filterDesc}\n\nTry find_skills without filters to see all available skills.`
148
+ : `No skills found.\n\nInstall locus skills or add personal skills to ${personalSkillsDir}/`;
149
+ }
150
+ let output = `Found ${allSkills.length} skill(s):\n\n`;
151
+ // Group by source type
152
+ const grouped = {
153
+ project: allSkills.filter(s => s.sourceType === 'project'),
154
+ personal: allSkills.filter(s => s.sourceType === 'personal'),
155
+ locus: allSkills.filter(s => s.sourceType === 'locus'),
156
+ };
157
+ for (const [sourceType, skills] of Object.entries(grouped)) {
158
+ if (skills.length === 0)
159
+ continue;
160
+ output += `## ${sourceType.charAt(0).toUpperCase() + sourceType.slice(1)} Skills\n\n`;
161
+ for (const skill of skills) {
162
+ const namespace = sourceType === 'personal' ? '' : `${sourceType}:`;
163
+ output += `**${namespace}${skill.name}**`;
164
+ if (skill.tier || skill.category) {
165
+ output += ` [${[skill.tier, skill.category].filter(Boolean).join('/')}]`;
166
+ }
167
+ output += '\n';
168
+ if (skill.description) {
169
+ output += ` ${skill.description}\n`;
170
+ }
171
+ }
172
+ output += '\n';
173
+ }
174
+ return output;
175
+ }
176
+ }),
177
+ /**
178
+ * List all available agents
179
+ */
180
+ find_agents: tool({
181
+ description: 'List all available agent definitions for specialized tasks.',
182
+ args: {},
183
+ execute: async () => {
184
+ const locusAgents = findAgentsInDir(locusAgentsDir, 3);
185
+ const projectAgents = findAgentsInDir(projectAgentsDir, 3);
186
+ const allAgents = [...projectAgents, ...locusAgents];
187
+ if (allAgents.length === 0) {
188
+ return 'No agents found.';
189
+ }
190
+ let output = 'Available agents:\n\n';
191
+ // Group by category
192
+ const byCategory = new Map();
193
+ for (const agent of allAgents) {
194
+ const cat = agent.category || 'general';
195
+ if (!byCategory.has(cat)) {
196
+ byCategory.set(cat, []);
197
+ }
198
+ byCategory.get(cat).push(agent);
199
+ }
200
+ for (const [category, agents] of byCategory) {
201
+ output += `## ${category.charAt(0).toUpperCase() + category.slice(1)}\n\n`;
202
+ for (const agent of agents) {
203
+ output += `**${agent.name}**\n`;
204
+ if (agent.description) {
205
+ // Truncate long descriptions
206
+ const desc = agent.description.length > 200
207
+ ? agent.description.substring(0, 200) + '...'
208
+ : agent.description;
209
+ output += ` ${desc}\n`;
210
+ }
211
+ output += ` Path: ${agent.path}\n\n`;
212
+ }
213
+ }
214
+ return output;
215
+ }
216
+ }),
217
+ },
218
+ /**
219
+ * Event handlers for session lifecycle
220
+ */
104
221
  event: async ({ event }) => {
105
- if (event.type === "session.created") {
106
- // Plugin is ready
222
+ // Extract sessionID from various event structures
223
+ const getSessionID = () => {
224
+ const props = event.properties;
225
+ const session = event.session;
226
+ return props?.info?.id ||
227
+ props?.sessionID ||
228
+ session?.id;
229
+ };
230
+ // Inject bootstrap at session creation (before first user message)
231
+ if (event.type === 'session.created') {
232
+ const sessionID = getSessionID();
233
+ if (sessionID) {
234
+ await injectBootstrap(sessionID, false);
235
+ }
236
+ }
237
+ // Re-inject bootstrap after context compaction (compact version to save tokens)
238
+ if (event.type === 'session.compacted') {
239
+ const sessionID = getSessionID();
240
+ if (sessionID) {
241
+ await injectBootstrap(sessionID, true);
242
+ }
107
243
  }
108
244
  },
109
245
  };
110
246
  };
111
247
  // Default export for OpenCode plugin loading
112
248
  export default LocusPlugin;
113
- // Named exports for programmatic use
114
- export { getSkillContent, getProjectState, listProjects, formatProjectStatus };
249
+ // Re-export utilities for programmatic use
250
+ export * from "./lib/skills-core.js";
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Skills Core Library
3
+ * Shared utilities for skill discovery, loading, and management
4
+ */
5
+ export interface SkillMetadata {
6
+ name: string;
7
+ description: string;
8
+ license?: string;
9
+ version?: string;
10
+ author?: string;
11
+ category?: string;
12
+ tier?: string;
13
+ council?: string;
14
+ tools?: string;
15
+ metadata?: {
16
+ version?: string;
17
+ tier?: string;
18
+ category?: string;
19
+ council?: string;
20
+ [key: string]: string | undefined;
21
+ };
22
+ [key: string]: string | Record<string, string | undefined> | undefined;
23
+ }
24
+ export interface SkillInfo {
25
+ path: string;
26
+ skillFile: string;
27
+ name: string;
28
+ description: string;
29
+ sourceType: 'project' | 'personal' | 'locus';
30
+ tier?: string;
31
+ category?: string;
32
+ council?: string;
33
+ version?: string;
34
+ }
35
+ export interface AgentInfo {
36
+ path: string;
37
+ name: string;
38
+ description: string;
39
+ model?: string;
40
+ category: string;
41
+ }
42
+ export interface ResolvedSkill {
43
+ skillFile: string;
44
+ sourceType: 'project' | 'personal' | 'locus';
45
+ skillPath: string;
46
+ }
47
+ /**
48
+ * Extract YAML frontmatter from a skill or agent file.
49
+ *
50
+ * Supports:
51
+ * - Simple key: value pairs
52
+ * - Multiline values with |
53
+ * - Nested objects (one level deep, e.g., metadata:)
54
+ *
55
+ * Format:
56
+ * ---
57
+ * name: skill-name
58
+ * description: Use when [condition] - [what it does]
59
+ * metadata:
60
+ * version: "1.0.0"
61
+ * tier: developer
62
+ * ---
63
+ */
64
+ export declare function extractFrontmatter(filePath: string): SkillMetadata;
65
+ /**
66
+ * Strip YAML frontmatter from content, returning just the body.
67
+ */
68
+ export declare function stripFrontmatter(content: string): string;
69
+ /**
70
+ * Find all SKILL.md files in a directory recursively.
71
+ */
72
+ export declare function findSkillsInDir(dir: string, sourceType: 'project' | 'personal' | 'locus', maxDepth?: number): SkillInfo[];
73
+ /**
74
+ * Find all agent definition files in a directory.
75
+ */
76
+ export declare function findAgentsInDir(dir: string, maxDepth?: number): AgentInfo[];
77
+ /**
78
+ * Resolve a skill name to its file path, handling namespacing and shadowing.
79
+ * Priority: project > personal > locus
80
+ *
81
+ * Skill names can be:
82
+ * - "skill-name" - searches all locations
83
+ * - "project:skill-name" - forces project skills only
84
+ * - "locus:skill-name" - forces locus skills only
85
+ */
86
+ export declare function resolveSkillPath(skillName: string, locusSkillsDir: string, personalSkillsDir: string | null, projectSkillsDir: string | null): ResolvedSkill | null;
87
+ /**
88
+ * Get the using-locus bootstrap skill content.
89
+ */
90
+ export declare function getBootstrapContent(locusSkillsDir: string, compact?: boolean): string | null;
91
+ /**
92
+ * Normalize a path: trim whitespace, expand ~, resolve to absolute.
93
+ */
94
+ export declare function normalizePath(p: string | undefined, homeDir: string): string | null;
95
+ //# sourceMappingURL=skills-core.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skills-core.d.ts","sourceRoot":"","sources":["../../src/lib/skills-core.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE;QACT,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;KACnC,CAAC;IACF,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;CACxE;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAC7C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,CAAC;IAC7C,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,aAAa,CA2GlE;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAsBxD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,UAAU,EAAE,SAAS,GAAG,UAAU,GAAG,OAAO,EAC5C,QAAQ,SAAI,GACX,SAAS,EAAE,CA8Cb;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,SAAI,GAAG,SAAS,EAAE,CAuCtE;AAED;;;;;;;;GAQG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,GAAG,IAAI,EAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAC9B,aAAa,GAAG,IAAI,CA4BtB;AAsCD;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,cAAc,EAAE,MAAM,EACtB,OAAO,UAAQ,GACd,MAAM,GAAG,IAAI,CA2Cf;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAanF"}