@liangjie559567/ultrapower 7.0.3 → 7.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +4 -0
- package/bridge/mcp-server.cjs +117 -68
- package/dist/hooks/autopilot/cancel.d.ts.map +1 -1
- package/dist/hooks/autopilot/cancel.js +15 -15
- package/dist/hooks/autopilot/cancel.js.map +1 -1
- package/dist/hooks/autopilot/enforcement.js +12 -12
- package/dist/hooks/autopilot/enforcement.js.map +1 -1
- package/dist/hooks/autopilot/state.d.ts.map +1 -1
- package/dist/hooks/autopilot/state.js +28 -30
- package/dist/hooks/autopilot/state.js.map +1 -1
- package/dist/hooks/bridge-types.d.ts +2 -0
- package/dist/hooks/bridge-types.d.ts.map +1 -1
- package/dist/hooks/bridge-types.js.map +1 -1
- package/dist/hooks/handlers/route-map.d.ts.map +1 -1
- package/dist/hooks/handlers/route-map.js +15 -0
- package/dist/hooks/handlers/route-map.js.map +1 -1
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/mode-cancel-sync.d.ts.map +1 -1
- package/dist/hooks/mode-cancel-sync.js +11 -12
- package/dist/hooks/mode-cancel-sync.js.map +1 -1
- package/dist/hooks/persistent-mode/index.js +2 -2
- package/dist/hooks/persistent-mode/index.js.map +1 -1
- package/dist/hooks/ralph/loop.d.ts.map +1 -1
- package/dist/hooks/ralph/loop.js +7 -8
- package/dist/hooks/ralph/loop.js.map +1 -1
- package/dist/hooks/ralph/verifier.js +11 -11
- package/dist/hooks/ralph/verifier.js.map +1 -1
- package/dist/hooks/ultrawork/index.d.ts.map +1 -1
- package/dist/hooks/ultrawork/index.js +7 -4
- package/dist/hooks/ultrawork/index.js.map +1 -1
- package/dist/hooks/validation.d.ts.map +1 -1
- package/dist/hooks/validation.js +2 -0
- package/dist/hooks/validation.js.map +1 -1
- package/dist/hooks/workflow-gate/index.d.ts +116 -0
- package/dist/hooks/workflow-gate/index.d.ts.map +1 -0
- package/dist/hooks/workflow-gate/index.js +352 -0
- package/dist/hooks/workflow-gate/index.js.map +1 -0
- package/dist/lib/state-cache.d.ts +8 -5
- package/dist/lib/state-cache.d.ts.map +1 -1
- package/dist/lib/state-cache.js +26 -16
- package/dist/lib/state-cache.js.map +1 -1
- package/dist/mcp/adapters/state-adapter.d.ts +8 -8
- package/dist/team/index.d.ts +1 -0
- package/dist/team/index.d.ts.map +1 -1
- package/dist/team/index.js +1 -0
- package/dist/team/index.js.map +1 -1
- package/dist/team/workflow-integration.d.ts +8 -0
- package/dist/team/workflow-integration.d.ts.map +1 -0
- package/dist/team/workflow-integration.js +62 -0
- package/dist/team/workflow-integration.js.map +1 -0
- package/dist/tools/lsp/client.d.ts +8 -2
- package/dist/tools/lsp/client.d.ts.map +1 -1
- package/dist/tools/lsp/client.js +114 -70
- package/dist/tools/lsp/client.js.map +1 -1
- package/dist/tools/lsp-tools.d.ts.map +1 -1
- package/dist/tools/lsp-tools.js +1 -4
- package/dist/tools/lsp-tools.js.map +1 -1
- package/docs/REFERENCE.md +3 -0
- package/package.json +1 -1
- package/skills/brainstorming/SKILL.md +57 -14
- package/skills/omc-doctor/SKILL.md +45 -0
- package/skills/team/SKILL.md +12 -0
package/dist/hooks/validation.js
CHANGED
|
@@ -17,6 +17,8 @@ export function requiredKeysForHook(hookType) {
|
|
|
17
17
|
return ["directory", "toolName"];
|
|
18
18
|
case "autopilot":
|
|
19
19
|
return ["directory"];
|
|
20
|
+
case "user-prompt-submit":
|
|
21
|
+
return ["prompt", "directory"];
|
|
20
22
|
case "keyword-detector":
|
|
21
23
|
case "stop-continuation":
|
|
22
24
|
return [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/hooks/validation.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,aAAa,CAAC;QACnB,KAAK,gBAAgB,CAAC;QACtB,KAAK,eAAe,CAAC;QACrB,KAAK,aAAa,CAAC;QACnB,KAAK,YAAY,CAAC;QAClB,KAAK,mBAAmB,CAAC;QACzB,KAAK,OAAO,CAAC;QACb,KAAK,iBAAiB,CAAC;QACvB,KAAK,eAAe;YAClB,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACpC,KAAK,oBAAoB;YACvB,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QAChD,KAAK,cAAc,CAAC;QACpB,KAAK,eAAe;YAClB,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACnC,KAAK,WAAW;YACd,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,KAAK,kBAAkB,CAAC;QACxB,KAAK,mBAAmB;YACtB,OAAO,EAAE,CAAC;QACZ;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAc,EACd,cAAwB,EACxB,QAAiB;IAEjB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CACnC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,CACvD,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,+CAA+C,QAAQ,IAAI,SAAS,oBAAoB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7G,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,IAAI,gBAAgB,GAAoB,IAAI,CAAC;AAE7C,MAAM,UAAU,YAAY;IAC1B,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACvG,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,gBAAgB,GAAG,IAAI,CAAC;AAC1B,CAAC"}
|
|
1
|
+
{"version":3,"file":"validation.js","sourceRoot":"","sources":["../../src/hooks/validation.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,mBAAmB,CAAC,QAAgB;IAClD,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,aAAa,CAAC;QACnB,KAAK,gBAAgB,CAAC;QACtB,KAAK,eAAe,CAAC;QACrB,KAAK,aAAa,CAAC;QACnB,KAAK,YAAY,CAAC;QAClB,KAAK,mBAAmB,CAAC;QACzB,KAAK,OAAO,CAAC;QACb,KAAK,iBAAiB,CAAC;QACvB,KAAK,eAAe;YAClB,OAAO,CAAC,WAAW,EAAE,WAAW,CAAC,CAAC;QACpC,KAAK,oBAAoB;YACvB,OAAO,CAAC,WAAW,EAAE,WAAW,EAAE,UAAU,CAAC,CAAC;QAChD,KAAK,cAAc,CAAC;QACpB,KAAK,eAAe;YAClB,OAAO,CAAC,WAAW,EAAE,UAAU,CAAC,CAAC;QACnC,KAAK,WAAW;YACd,OAAO,CAAC,WAAW,CAAC,CAAC;QACvB,KAAK,oBAAoB;YACvB,OAAO,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;QACjC,KAAK,kBAAkB,CAAC;QACxB,KAAK,mBAAmB;YACtB,OAAO,EAAE,CAAC;QACZ;YACE,OAAO,EAAE,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,iBAAiB,CAC/B,KAAc,EACd,cAAwB,EACxB,QAAiB;IAEjB,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC9D,MAAM,GAAG,GAAG,KAAgC,CAAC;IAC7C,MAAM,OAAO,GAAG,cAAc,CAAC,MAAM,CACnC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,SAAS,CACvD,CAAC;IACF,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CACX,+CAA+C,QAAQ,IAAI,SAAS,oBAAoB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAC7G,CAAC;QACF,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,IAAI,gBAAgB,GAAoB,IAAI,CAAC;AAE7C,MAAM,UAAU,YAAY;IAC1B,IAAI,gBAAgB,KAAK,IAAI,EAAE,CAAC;QAC9B,gBAAgB,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;IACvG,CAAC;IACD,OAAO,gBAAgB,CAAC;AAC1B,CAAC;AAED,MAAM,UAAU,mBAAmB;IACjC,gBAAgB,GAAG,IAAI,CAAC;AAC1B,CAAC"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Gate Hook
|
|
3
|
+
*
|
|
4
|
+
* Enforces superpowers workflow discipline by detecting when users
|
|
5
|
+
* skip required steps and automatically injecting the correct skill.
|
|
6
|
+
*
|
|
7
|
+
* Workflow stages:
|
|
8
|
+
* 1. brainstorming (required before any implementation)
|
|
9
|
+
* 2. writing-plans (required after brainstorming)
|
|
10
|
+
* 3. using-git-worktrees (recommended before execution)
|
|
11
|
+
* 4. execution (subagent-driven-development or executing-plans)
|
|
12
|
+
* 5. requesting-code-review (required before completion)
|
|
13
|
+
* 6. verification-before-completion (required before merge)
|
|
14
|
+
*/
|
|
15
|
+
export interface WorkflowState {
|
|
16
|
+
brainstormingComplete: boolean;
|
|
17
|
+
planWritten: boolean;
|
|
18
|
+
worktreeCreated: boolean;
|
|
19
|
+
testsWritten: boolean;
|
|
20
|
+
executionStarted: boolean;
|
|
21
|
+
reviewRequested: boolean;
|
|
22
|
+
codeReviewComplete: boolean;
|
|
23
|
+
securityReviewComplete: boolean;
|
|
24
|
+
performanceReviewComplete: boolean;
|
|
25
|
+
verificationComplete: boolean;
|
|
26
|
+
lastStage: string;
|
|
27
|
+
timestamp: number;
|
|
28
|
+
}
|
|
29
|
+
export interface WorkflowGateInput {
|
|
30
|
+
type: 'UserPromptSubmit';
|
|
31
|
+
prompt: string;
|
|
32
|
+
workingDirectory: string;
|
|
33
|
+
}
|
|
34
|
+
export interface WorkflowGateOutput {
|
|
35
|
+
success: boolean;
|
|
36
|
+
shouldBlock: boolean;
|
|
37
|
+
injectedSkill?: string;
|
|
38
|
+
message?: string;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get workflow state file path
|
|
42
|
+
*/
|
|
43
|
+
export declare function getWorkflowStatePath(workingDir: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Read current workflow state
|
|
46
|
+
*/
|
|
47
|
+
export declare function readWorkflowState(workingDir: string): WorkflowState | null;
|
|
48
|
+
/**
|
|
49
|
+
* Write workflow state
|
|
50
|
+
*/
|
|
51
|
+
export declare function writeWorkflowState(workingDir: string, state: WorkflowState): void;
|
|
52
|
+
/**
|
|
53
|
+
* Initialize workflow state
|
|
54
|
+
*/
|
|
55
|
+
export declare function initWorkflowState(workingDir: string): WorkflowState;
|
|
56
|
+
/**
|
|
57
|
+
* Detect implementation keywords that require brainstorming first
|
|
58
|
+
*/
|
|
59
|
+
export declare function detectImplementationIntent(prompt: string): boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Detect plan execution keywords
|
|
62
|
+
*/
|
|
63
|
+
export declare function detectExecutionIntent(prompt: string): boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Check if brainstorming skill was just completed
|
|
66
|
+
*/
|
|
67
|
+
export declare function detectBrainstormingComplete(prompt: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Check if plan was just written
|
|
70
|
+
*/
|
|
71
|
+
export declare function detectPlanComplete(prompt: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Check if tests were just written
|
|
74
|
+
*/
|
|
75
|
+
export declare function detectTestsComplete(prompt: string): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Check if code review was completed
|
|
78
|
+
*/
|
|
79
|
+
export declare function detectCodeReviewComplete(prompt: string): boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Check if security review was completed
|
|
82
|
+
*/
|
|
83
|
+
export declare function detectSecurityReviewComplete(prompt: string): boolean;
|
|
84
|
+
/**
|
|
85
|
+
* Check if performance review was completed
|
|
86
|
+
*/
|
|
87
|
+
export declare function detectPerformanceReviewComplete(prompt: string): boolean;
|
|
88
|
+
/**
|
|
89
|
+
* Detect if prompt contains security-sensitive keywords
|
|
90
|
+
*/
|
|
91
|
+
export declare function detectSecuritySensitive(prompt: string): boolean;
|
|
92
|
+
/**
|
|
93
|
+
* Detect if prompt contains performance-sensitive keywords
|
|
94
|
+
*/
|
|
95
|
+
export declare function detectPerformanceSensitive(prompt: string): boolean;
|
|
96
|
+
/**
|
|
97
|
+
* Detect if user is trying to use executing-plans or subagent-driven-development
|
|
98
|
+
*/
|
|
99
|
+
export declare function detectPlanExecutionSkill(prompt: string): boolean;
|
|
100
|
+
/**
|
|
101
|
+
* Detect if user is asking a vague question that needs brainstorming
|
|
102
|
+
*/
|
|
103
|
+
export declare function detectVagueRequest(prompt: string): boolean;
|
|
104
|
+
/**
|
|
105
|
+
* Suggest next step based on current workflow state
|
|
106
|
+
*/
|
|
107
|
+
export declare function suggestNextStep(state: WorkflowState): string | null;
|
|
108
|
+
/**
|
|
109
|
+
* Process workflow gate check
|
|
110
|
+
*/
|
|
111
|
+
export declare function processWorkflowGate(input: WorkflowGateInput): WorkflowGateOutput;
|
|
112
|
+
/**
|
|
113
|
+
* Clear workflow state (for testing or manual reset)
|
|
114
|
+
*/
|
|
115
|
+
export declare function clearWorkflowState(workingDir: string): void;
|
|
116
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/hooks/workflow-gate/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAKH,MAAM,WAAW,aAAa;IAC5B,qBAAqB,EAAE,OAAO,CAAC;IAC/B,WAAW,EAAE,OAAO,CAAC;IACrB,eAAe,EAAE,OAAO,CAAC;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,eAAe,EAAE,OAAO,CAAC;IACzB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,sBAAsB,EAAE,OAAO,CAAC;IAChC,yBAAyB,EAAE,OAAO,CAAC;IACnC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,kBAAkB,CAAC;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,OAAO,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAID;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAE/D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAY1E;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,GAAG,IAAI,CASjF;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,CAkBnE;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CASlE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAQ7D;AAED;;GAEG;AACH,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGnE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG1D;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAG3D;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGhE;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGpE;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAGvE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAO/D;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAOlE;AAED;;GAEG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKhE;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAuB1D;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAkCnE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAwIhF;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAK3D"}
|
|
@@ -0,0 +1,352 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Gate Hook
|
|
3
|
+
*
|
|
4
|
+
* Enforces superpowers workflow discipline by detecting when users
|
|
5
|
+
* skip required steps and automatically injecting the correct skill.
|
|
6
|
+
*
|
|
7
|
+
* Workflow stages:
|
|
8
|
+
* 1. brainstorming (required before any implementation)
|
|
9
|
+
* 2. writing-plans (required after brainstorming)
|
|
10
|
+
* 3. using-git-worktrees (recommended before execution)
|
|
11
|
+
* 4. execution (subagent-driven-development or executing-plans)
|
|
12
|
+
* 5. requesting-code-review (required before completion)
|
|
13
|
+
* 6. verification-before-completion (required before merge)
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, existsSync, writeFileSync } from 'fs';
|
|
16
|
+
import { join } from 'path';
|
|
17
|
+
const WORKFLOW_STATE_FILE = '.omc/workflow-state.json';
|
|
18
|
+
/**
|
|
19
|
+
* Get workflow state file path
|
|
20
|
+
*/
|
|
21
|
+
export function getWorkflowStatePath(workingDir) {
|
|
22
|
+
return join(workingDir, WORKFLOW_STATE_FILE);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Read current workflow state
|
|
26
|
+
*/
|
|
27
|
+
export function readWorkflowState(workingDir) {
|
|
28
|
+
const statePath = getWorkflowStatePath(workingDir);
|
|
29
|
+
if (!existsSync(statePath)) {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
try {
|
|
33
|
+
const content = readFileSync(statePath, 'utf-8');
|
|
34
|
+
return JSON.parse(content);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Write workflow state
|
|
42
|
+
*/
|
|
43
|
+
export function writeWorkflowState(workingDir, state) {
|
|
44
|
+
const statePath = getWorkflowStatePath(workingDir);
|
|
45
|
+
const dir = join(workingDir, '.omc');
|
|
46
|
+
if (!existsSync(dir)) {
|
|
47
|
+
require('fs').mkdirSync(dir, { recursive: true });
|
|
48
|
+
}
|
|
49
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf-8');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Initialize workflow state
|
|
53
|
+
*/
|
|
54
|
+
export function initWorkflowState(workingDir) {
|
|
55
|
+
const state = {
|
|
56
|
+
brainstormingComplete: false,
|
|
57
|
+
planWritten: false,
|
|
58
|
+
worktreeCreated: false,
|
|
59
|
+
testsWritten: false,
|
|
60
|
+
executionStarted: false,
|
|
61
|
+
reviewRequested: false,
|
|
62
|
+
codeReviewComplete: false,
|
|
63
|
+
securityReviewComplete: false,
|
|
64
|
+
performanceReviewComplete: false,
|
|
65
|
+
verificationComplete: false,
|
|
66
|
+
lastStage: 'init',
|
|
67
|
+
timestamp: Date.now()
|
|
68
|
+
};
|
|
69
|
+
writeWorkflowState(workingDir, state);
|
|
70
|
+
return state;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Detect implementation keywords that require brainstorming first
|
|
74
|
+
*/
|
|
75
|
+
export function detectImplementationIntent(prompt) {
|
|
76
|
+
const implementationKeywords = [
|
|
77
|
+
'implement', 'create', 'add', 'build', 'develop',
|
|
78
|
+
'write code', 'make a', 'generate', 'construct',
|
|
79
|
+
'实现', '创建', '添加', '构建', '开发', '编写代码', '生成'
|
|
80
|
+
];
|
|
81
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
82
|
+
return implementationKeywords.some(kw => lowerPrompt.includes(kw));
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Detect plan execution keywords
|
|
86
|
+
*/
|
|
87
|
+
export function detectExecutionIntent(prompt) {
|
|
88
|
+
const executionKeywords = [
|
|
89
|
+
'execute', 'run', 'start', 'begin implementation',
|
|
90
|
+
'执行', '运行', '开始实现'
|
|
91
|
+
];
|
|
92
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
93
|
+
return executionKeywords.some(kw => lowerPrompt.includes(kw));
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Check if brainstorming skill was just completed
|
|
97
|
+
*/
|
|
98
|
+
export function detectBrainstormingComplete(prompt) {
|
|
99
|
+
return prompt.includes('brainstorming') &&
|
|
100
|
+
(prompt.includes('complete') || prompt.includes('完成'));
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Check if plan was just written
|
|
104
|
+
*/
|
|
105
|
+
export function detectPlanComplete(prompt) {
|
|
106
|
+
return (prompt.includes('plan') || prompt.includes('writing-plans')) &&
|
|
107
|
+
(prompt.includes('complete') || prompt.includes('written') || prompt.includes('完成'));
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Check if tests were just written
|
|
111
|
+
*/
|
|
112
|
+
export function detectTestsComplete(prompt) {
|
|
113
|
+
return (prompt.includes('test') || prompt.includes('测试')) &&
|
|
114
|
+
(prompt.includes('complete') || prompt.includes('written') || prompt.includes('完成'));
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Check if code review was completed
|
|
118
|
+
*/
|
|
119
|
+
export function detectCodeReviewComplete(prompt) {
|
|
120
|
+
return (prompt.includes('code review') || prompt.includes('代码审查')) &&
|
|
121
|
+
(prompt.includes('complete') || prompt.includes('完成'));
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Check if security review was completed
|
|
125
|
+
*/
|
|
126
|
+
export function detectSecurityReviewComplete(prompt) {
|
|
127
|
+
return (prompt.includes('security review') || prompt.includes('安全审查')) &&
|
|
128
|
+
(prompt.includes('complete') || prompt.includes('完成'));
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Check if performance review was completed
|
|
132
|
+
*/
|
|
133
|
+
export function detectPerformanceReviewComplete(prompt) {
|
|
134
|
+
return (prompt.includes('performance review') || prompt.includes('性能审查')) &&
|
|
135
|
+
(prompt.includes('complete') || prompt.includes('完成'));
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Detect if prompt contains security-sensitive keywords
|
|
139
|
+
*/
|
|
140
|
+
export function detectSecuritySensitive(prompt) {
|
|
141
|
+
const securityKeywords = [
|
|
142
|
+
'auth', 'password', 'token', 'jwt', 'session', 'crypto', 'encrypt',
|
|
143
|
+
'认证', '密码', '加密', '令牌'
|
|
144
|
+
];
|
|
145
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
146
|
+
return securityKeywords.some(kw => lowerPrompt.includes(kw) || prompt.includes(kw));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Detect if prompt contains performance-sensitive keywords
|
|
150
|
+
*/
|
|
151
|
+
export function detectPerformanceSensitive(prompt) {
|
|
152
|
+
const performanceKeywords = [
|
|
153
|
+
'performance', 'optimize', 'cache', 'query', 'database', 'api',
|
|
154
|
+
'性能', '优化', '缓存', '查询', '数据库'
|
|
155
|
+
];
|
|
156
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
157
|
+
return performanceKeywords.some(kw => lowerPrompt.includes(kw) || prompt.includes(kw));
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Detect if user is trying to use executing-plans or subagent-driven-development
|
|
161
|
+
*/
|
|
162
|
+
export function detectPlanExecutionSkill(prompt) {
|
|
163
|
+
return prompt.includes('executing-plans') ||
|
|
164
|
+
prompt.includes('subagent-driven-development') ||
|
|
165
|
+
prompt.includes('/ultrapower:executing-plans') ||
|
|
166
|
+
prompt.includes('/ultrapower:subagent-driven-development');
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Detect if user is asking a vague question that needs brainstorming
|
|
170
|
+
*/
|
|
171
|
+
export function detectVagueRequest(prompt) {
|
|
172
|
+
const vaguePatterns = [
|
|
173
|
+
'how to', 'what should', 'help me', 'i want to', 'i need',
|
|
174
|
+
'如何', '怎么', '帮我', '我想', '我需要'
|
|
175
|
+
];
|
|
176
|
+
// Check both original and lowercase for case-insensitive English matching
|
|
177
|
+
const lowerPrompt = prompt.toLowerCase();
|
|
178
|
+
const hasVaguePattern = vaguePatterns.some(pattern => lowerPrompt.includes(pattern.toLowerCase()) || prompt.includes(pattern));
|
|
179
|
+
// Ignore if brainstorming already mentioned
|
|
180
|
+
if (prompt.includes('brainstorm')) {
|
|
181
|
+
return false;
|
|
182
|
+
}
|
|
183
|
+
// Require minimum length (count characters, not bytes)
|
|
184
|
+
if ([...prompt].length < 8) {
|
|
185
|
+
return false;
|
|
186
|
+
}
|
|
187
|
+
return hasVaguePattern;
|
|
188
|
+
}
|
|
189
|
+
/**
|
|
190
|
+
* Suggest next step based on current workflow state
|
|
191
|
+
*/
|
|
192
|
+
export function suggestNextStep(state) {
|
|
193
|
+
if (!state.brainstormingComplete) {
|
|
194
|
+
return 'brainstorming';
|
|
195
|
+
}
|
|
196
|
+
if (!state.planWritten) {
|
|
197
|
+
return 'writing-plans';
|
|
198
|
+
}
|
|
199
|
+
if (!state.testsWritten && state.planWritten) {
|
|
200
|
+
return 'test-driven-development';
|
|
201
|
+
}
|
|
202
|
+
if (!state.executionStarted && state.testsWritten) {
|
|
203
|
+
return 'subagent-driven-development';
|
|
204
|
+
}
|
|
205
|
+
if (!state.reviewRequested && state.executionStarted) {
|
|
206
|
+
return 'requesting-code-review';
|
|
207
|
+
}
|
|
208
|
+
if (!state.codeReviewComplete && state.reviewRequested) {
|
|
209
|
+
return 'code-review';
|
|
210
|
+
}
|
|
211
|
+
if (!state.verificationComplete && state.codeReviewComplete) {
|
|
212
|
+
return 'verification-before-completion';
|
|
213
|
+
}
|
|
214
|
+
if (!state.worktreeCreated && state.planWritten) {
|
|
215
|
+
return 'using-git-worktrees';
|
|
216
|
+
}
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Process workflow gate check
|
|
221
|
+
*/
|
|
222
|
+
export function processWorkflowGate(input) {
|
|
223
|
+
const { prompt, workingDirectory } = input;
|
|
224
|
+
let state = readWorkflowState(workingDirectory);
|
|
225
|
+
if (!state) {
|
|
226
|
+
state = initWorkflowState(workingDirectory);
|
|
227
|
+
}
|
|
228
|
+
// Update state based on prompt
|
|
229
|
+
if (detectBrainstormingComplete(prompt)) {
|
|
230
|
+
state.brainstormingComplete = true;
|
|
231
|
+
state.lastStage = 'brainstorming';
|
|
232
|
+
state.timestamp = Date.now();
|
|
233
|
+
writeWorkflowState(workingDirectory, state);
|
|
234
|
+
}
|
|
235
|
+
if (detectPlanComplete(prompt)) {
|
|
236
|
+
state.planWritten = true;
|
|
237
|
+
state.lastStage = 'planning';
|
|
238
|
+
state.timestamp = Date.now();
|
|
239
|
+
writeWorkflowState(workingDirectory, state);
|
|
240
|
+
}
|
|
241
|
+
if (detectTestsComplete(prompt)) {
|
|
242
|
+
state.testsWritten = true;
|
|
243
|
+
state.lastStage = 'testing';
|
|
244
|
+
state.timestamp = Date.now();
|
|
245
|
+
writeWorkflowState(workingDirectory, state);
|
|
246
|
+
}
|
|
247
|
+
if (detectCodeReviewComplete(prompt)) {
|
|
248
|
+
state.codeReviewComplete = true;
|
|
249
|
+
state.lastStage = 'code-review';
|
|
250
|
+
state.timestamp = Date.now();
|
|
251
|
+
writeWorkflowState(workingDirectory, state);
|
|
252
|
+
}
|
|
253
|
+
if (detectSecurityReviewComplete(prompt)) {
|
|
254
|
+
state.securityReviewComplete = true;
|
|
255
|
+
state.lastStage = 'security-review';
|
|
256
|
+
state.timestamp = Date.now();
|
|
257
|
+
writeWorkflowState(workingDirectory, state);
|
|
258
|
+
}
|
|
259
|
+
if (detectPerformanceReviewComplete(prompt)) {
|
|
260
|
+
state.performanceReviewComplete = true;
|
|
261
|
+
state.lastStage = 'performance-review';
|
|
262
|
+
state.timestamp = Date.now();
|
|
263
|
+
writeWorkflowState(workingDirectory, state);
|
|
264
|
+
}
|
|
265
|
+
// Gate 1: Implementation without brainstorming (highest priority)
|
|
266
|
+
if (detectImplementationIntent(prompt) && !state.brainstormingComplete) {
|
|
267
|
+
return {
|
|
268
|
+
success: true,
|
|
269
|
+
shouldBlock: true,
|
|
270
|
+
injectedSkill: 'brainstorming',
|
|
271
|
+
message: '⚠️ Workflow Gate: 在实现之前必须先进行头脑风暴。自动注入 brainstorming skill。'
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
// Gate 2: Execution without plan
|
|
275
|
+
if (detectExecutionIntent(prompt) && !state.planWritten) {
|
|
276
|
+
return {
|
|
277
|
+
success: true,
|
|
278
|
+
shouldBlock: true,
|
|
279
|
+
injectedSkill: 'writing-plans',
|
|
280
|
+
message: '⚠️ Workflow Gate: 在执行之前必须先编写计划。自动注入 writing-plans skill。'
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
// Gate 2.5: Execution without tests (TDD enforcement)
|
|
284
|
+
if (detectExecutionIntent(prompt) && state.planWritten && !state.testsWritten) {
|
|
285
|
+
return {
|
|
286
|
+
success: true,
|
|
287
|
+
shouldBlock: true,
|
|
288
|
+
injectedSkill: 'test-driven-development',
|
|
289
|
+
message: '⚠️ Workflow Gate: TDD 要求在实现前先编写测试。自动注入 test-driven-development skill。'
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
// Gate 3: Using executing-plans or subagent-driven-development without plan
|
|
293
|
+
if (detectPlanExecutionSkill(prompt) && !state.planWritten) {
|
|
294
|
+
return {
|
|
295
|
+
success: true,
|
|
296
|
+
shouldBlock: true,
|
|
297
|
+
injectedSkill: 'writing-plans',
|
|
298
|
+
message: '⚠️ Workflow Gate: 使用 executing-plans 或 subagent-driven-development 前必须先编写计划。自动注入 writing-plans skill。'
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
// Gate 4: Verification without code review
|
|
302
|
+
if (prompt.includes('verification') && state.executionStarted && !state.codeReviewComplete) {
|
|
303
|
+
return {
|
|
304
|
+
success: true,
|
|
305
|
+
shouldBlock: true,
|
|
306
|
+
injectedSkill: 'code-review',
|
|
307
|
+
message: '⚠️ Workflow Gate: 验证前必须先完成代码审查。自动注入 code-review skill。'
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
// Gate 5: Security review for sensitive code
|
|
311
|
+
if (detectSecuritySensitive(prompt) && state.executionStarted && !state.securityReviewComplete) {
|
|
312
|
+
return {
|
|
313
|
+
success: true,
|
|
314
|
+
shouldBlock: true,
|
|
315
|
+
injectedSkill: 'security-review',
|
|
316
|
+
message: '⚠️ Workflow Gate: 检测到安全敏感代码,必须进行安全审查。自动注入 security-review skill。'
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
// Gate 6: Performance review for performance-sensitive code
|
|
320
|
+
if (detectPerformanceSensitive(prompt) && state.executionStarted && !state.performanceReviewComplete) {
|
|
321
|
+
return {
|
|
322
|
+
success: true,
|
|
323
|
+
shouldBlock: true,
|
|
324
|
+
injectedSkill: 'performance-review',
|
|
325
|
+
message: '⚠️ Workflow Gate: 检测到性能敏感代码,必须进行性能审查。自动注入 performance-review skill。'
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
// Auto-trigger: Suggest brainstorming for vague requests (lower priority)
|
|
329
|
+
// Only trigger if no implementation intent detected
|
|
330
|
+
if (detectVagueRequest(prompt) && !state.brainstormingComplete && !detectImplementationIntent(prompt)) {
|
|
331
|
+
return {
|
|
332
|
+
success: true,
|
|
333
|
+
shouldBlock: true,
|
|
334
|
+
injectedSkill: 'brainstorming',
|
|
335
|
+
message: '💡 自动触发: 检测到模糊需求,建议先进行头脑风暴以明确方向。'
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
return {
|
|
339
|
+
success: true,
|
|
340
|
+
shouldBlock: false
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Clear workflow state (for testing or manual reset)
|
|
345
|
+
*/
|
|
346
|
+
export function clearWorkflowState(workingDir) {
|
|
347
|
+
const statePath = getWorkflowStatePath(workingDir);
|
|
348
|
+
if (existsSync(statePath)) {
|
|
349
|
+
require('fs').unlinkSync(statePath);
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/workflow-gate/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,15 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* State Cache with
|
|
2
|
+
* State Cache with Lazy mtime Validation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* to
|
|
4
|
+
* Optimizations:
|
|
5
|
+
* - Delays mtime checks to once per second (reduces I/O by 100-200x)
|
|
6
|
+
* - Uses Object.freeze() instead of shallow copy (eliminates copy overhead)
|
|
7
|
+
* - Fast path: pure memory lookup when cache is fresh
|
|
8
|
+
* - Slow path: mtime validation only when needed
|
|
6
9
|
*/
|
|
7
10
|
/**
|
|
8
|
-
* Read state with cache and mtime validation
|
|
11
|
+
* Read state with cache and lazy mtime validation
|
|
9
12
|
* @param path - File path to read
|
|
10
13
|
* @param data - Parsed JSON data (if already read)
|
|
11
14
|
* @param ttl - Cache TTL in milliseconds (default: 5000ms)
|
|
12
|
-
* @returns
|
|
15
|
+
* @returns Frozen cached data (immutable)
|
|
13
16
|
*/
|
|
14
17
|
export declare function readStateWithCache(path: string, data: unknown, ttl?: number): unknown;
|
|
15
18
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/lib/state-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"state-cache.d.ts","sourceRoot":"","sources":["../../src/lib/state-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAaH;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,GAAE,MAAa,GAAG,OAAO,CA8B3F;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEvD;AAED;;GAEG;AACH,wBAAgB,kBAAkB;;;EAKjC"}
|
package/dist/lib/state-cache.js
CHANGED
|
@@ -1,33 +1,43 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* State Cache with
|
|
2
|
+
* State Cache with Lazy mtime Validation
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* to
|
|
4
|
+
* Optimizations:
|
|
5
|
+
* - Delays mtime checks to once per second (reduces I/O by 100-200x)
|
|
6
|
+
* - Uses Object.freeze() instead of shallow copy (eliminates copy overhead)
|
|
7
|
+
* - Fast path: pure memory lookup when cache is fresh
|
|
8
|
+
* - Slow path: mtime validation only when needed
|
|
6
9
|
*/
|
|
7
10
|
import { statSync } from 'fs';
|
|
8
11
|
const stateCache = new Map();
|
|
9
12
|
/**
|
|
10
|
-
* Read state with cache and mtime validation
|
|
13
|
+
* Read state with cache and lazy mtime validation
|
|
11
14
|
* @param path - File path to read
|
|
12
15
|
* @param data - Parsed JSON data (if already read)
|
|
13
16
|
* @param ttl - Cache TTL in milliseconds (default: 5000ms)
|
|
14
|
-
* @returns
|
|
17
|
+
* @returns Frozen cached data (immutable)
|
|
15
18
|
*/
|
|
16
19
|
export function readStateWithCache(path, data, ttl = 5000) {
|
|
20
|
+
const now = Date.now();
|
|
21
|
+
const cached = stateCache.get(path);
|
|
22
|
+
// Fast path: cache hit within TTL, skip mtime check
|
|
23
|
+
if (cached && now - cached.cachedAt < ttl) {
|
|
24
|
+
// Only check mtime if we haven't checked recently (every 1s)
|
|
25
|
+
if (now - cached.mtimeCheckedAt < 1000) {
|
|
26
|
+
return Object.freeze(cached.data);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
// Slow path: validate mtime
|
|
17
30
|
try {
|
|
18
31
|
const mtime = statSync(path).mtimeMs;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
if (typeof cached.data === 'object' && cached.data !== null) {
|
|
24
|
-
return { ...cached.data };
|
|
25
|
-
}
|
|
26
|
-
return cached.data;
|
|
32
|
+
if (cached && cached.mtime === mtime) {
|
|
33
|
+
// Update mtime check timestamp
|
|
34
|
+
cached.mtimeCheckedAt = now;
|
|
35
|
+
return Object.freeze(cached.data);
|
|
27
36
|
}
|
|
28
|
-
// Cache miss: update cache
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
// Cache miss or stale: update cache
|
|
38
|
+
const frozenData = Object.freeze(data);
|
|
39
|
+
stateCache.set(path, { data: frozenData, mtime, cachedAt: now, mtimeCheckedAt: now });
|
|
40
|
+
return frozenData;
|
|
31
41
|
}
|
|
32
42
|
catch {
|
|
33
43
|
// If stat fails, return data without caching
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"state-cache.js","sourceRoot":"","sources":["../../src/lib/state-cache.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"state-cache.js","sourceRoot":"","sources":["../../src/lib/state-cache.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,CAAC;AAS9B,MAAM,UAAU,GAAG,IAAI,GAAG,EAAsB,CAAC;AAEjD;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,IAAa,EAAE,MAAc,IAAI;IAChF,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACvB,MAAM,MAAM,GAAG,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAEpC,oDAAoD;IACpD,IAAI,MAAM,IAAI,GAAG,GAAG,MAAM,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC;QAC1C,6DAA6D;QAC7D,IAAI,GAAG,GAAG,MAAM,CAAC,cAAc,GAAG,IAAI,EAAE,CAAC;YACvC,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;IACH,CAAC;IAED,4BAA4B;IAC5B,IAAI,CAAC;QACH,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC;QAErC,IAAI,MAAM,IAAI,MAAM,CAAC,KAAK,KAAK,KAAK,EAAE,CAAC;YACrC,+BAA+B;YAC/B,MAAM,CAAC,cAAc,GAAG,GAAG,CAAC;YAC5B,OAAO,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpC,CAAC;QAED,oCAAoC;QACpC,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACvC,UAAU,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,cAAc,EAAE,GAAG,EAAE,CAAC,CAAC;QACtF,OAAO,UAAU,CAAC;IACpB,CAAC;IAAC,MAAM,CAAC;QACP,6CAA6C;QAC7C,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB;IAChC,OAAO;QACL,IAAI,EAAE,UAAU,CAAC,IAAI;QACrB,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;KACvC,CAAC;AACJ,CAAC"}
|