oh-my-claude-sisyphus 3.6.3 → 3.7.2
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/README.md +40 -1
- package/commands/hud.md +37 -5
- package/commands/omc-setup.md +105 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
- package/dist/__tests__/hud/analytics-display.test.js +137 -1
- package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
- package/dist/__tests__/hud-windows.test.d.ts +2 -0
- package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
- package/dist/__tests__/hud-windows.test.js +91 -0
- package/dist/__tests__/hud-windows.test.js.map +1 -0
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
- package/dist/cli/commands/wait.d.ts +52 -0
- package/dist/cli/commands/wait.d.ts.map +1 -0
- package/dist/cli/commands/wait.js +229 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/index.js +54 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/daemon.js +585 -0
- package/dist/features/rate-limit-wait/daemon.js.map +1 -0
- package/dist/features/rate-limit-wait/index.d.ts +16 -0
- package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/index.js +18 -0
- package/dist/features/rate-limit-wait/index.js.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
- package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
- package/dist/features/rate-limit-wait/types.d.ts +121 -0
- package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/types.js +8 -0
- package/dist/features/rate-limit-wait/types.js.map +1 -0
- package/dist/features/state-manager/index.d.ts.map +1 -1
- package/dist/features/state-manager/index.js +4 -1
- package/dist/features/state-manager/index.js.map +1 -1
- package/dist/hooks/bridge.d.ts +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +50 -4
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
- package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
- package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/index.js +95 -8
- package/dist/hooks/omc-orchestrator/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js +291 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
- package/dist/hooks/permission-handler/index.d.ts +42 -0
- package/dist/hooks/permission-handler/index.d.ts.map +1 -0
- package/dist/hooks/permission-handler/index.js +107 -0
- package/dist/hooks/permission-handler/index.js.map +1 -0
- package/dist/hooks/plugin-patterns/index.d.ts +5 -0
- package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
- package/dist/hooks/plugin-patterns/index.js +26 -1
- package/dist/hooks/plugin-patterns/index.js.map +1 -1
- package/dist/hooks/pre-compact/index.d.ts +82 -0
- package/dist/hooks/pre-compact/index.d.ts.map +1 -0
- package/dist/hooks/pre-compact/index.js +265 -0
- package/dist/hooks/pre-compact/index.js.map +1 -0
- package/dist/hooks/session-end/index.d.ts +42 -0
- package/dist/hooks/session-end/index.d.ts.map +1 -0
- package/dist/hooks/session-end/index.js +200 -0
- package/dist/hooks/session-end/index.js.map +1 -0
- package/dist/hooks/setup/index.d.ts +66 -0
- package/dist/hooks/setup/index.d.ts.map +1 -0
- package/dist/hooks/setup/index.js +299 -0
- package/dist/hooks/setup/index.js.map +1 -0
- package/dist/hooks/setup/types.d.ts +25 -0
- package/dist/hooks/setup/types.d.ts.map +1 -0
- package/dist/hooks/setup/types.js +5 -0
- package/dist/hooks/setup/types.js.map +1 -0
- package/dist/hooks/subagent-tracker/index.d.ts +68 -29
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +316 -131
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/hud/analytics-display.d.ts +16 -0
- package/dist/hud/analytics-display.d.ts.map +1 -1
- package/dist/hud/analytics-display.js +35 -9
- package/dist/hud/analytics-display.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +49 -18
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +14 -0
- package/dist/hud/types.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +4 -3
- package/dist/installer/index.js.map +1 -1
- package/hooks/hooks.json +83 -1
- package/hooks/keyword-detector.sh +4 -4
- package/hooks/persistent-mode.sh +10 -10
- package/hooks/session-start.sh +4 -4
- package/package.json +3 -1
- package/scripts/keyword-detector.mjs +4 -4
- package/scripts/permission-handler.mjs +23 -0
- package/scripts/persistent-mode.mjs +6 -6
- package/scripts/persistent-mode.sh +10 -10
- package/scripts/pre-compact.mjs +23 -0
- package/scripts/session-end.mjs +23 -0
- package/scripts/session-start.mjs +4 -4
- package/scripts/setup-init.mjs +23 -0
- package/scripts/setup-maintenance.mjs +23 -0
- package/scripts/subagent-tracker.mjs +35 -0
- package/skills/hud/SKILL.md +37 -5
- package/skills/omc-setup/SKILL.md +162 -4
- package/skills/writer-memory/SKILL.md +443 -0
- package/skills/writer-memory/lib/character-tracker.ts +338 -0
- package/skills/writer-memory/lib/memory-manager.ts +804 -0
- package/skills/writer-memory/lib/relationship-graph.ts +400 -0
- package/skills/writer-memory/lib/scene-organizer.ts +544 -0
- package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
- package/skills/writer-memory/templates/synopsis-template.md +46 -0
- package/templates/hooks/keyword-detector.mjs +198 -0
- package/templates/hooks/keyword-detector.sh +102 -0
- package/templates/hooks/persistent-mode.mjs +249 -0
- package/templates/hooks/persistent-mode.sh +187 -0
- package/templates/hooks/post-tool-use.mjs +133 -0
- package/templates/hooks/post-tool-use.sh +90 -0
- package/templates/hooks/pre-tool-use.mjs +145 -0
- package/templates/hooks/pre-tool-use.sh +113 -0
- package/templates/hooks/session-start.mjs +100 -0
- package/templates/hooks/session-start.sh +62 -0
- package/templates/hooks/stop-continuation.mjs +80 -0
- package/templates/hooks/stop-continuation.sh +40 -0
- package/templates/rules/README.md +40 -0
- package/templates/rules/coding-style.md +74 -0
- package/templates/rules/git-workflow.md +41 -0
- package/templates/rules/performance.md +40 -0
- package/templates/rules/security.md +41 -0
- package/templates/rules/testing.md +42 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* OMC Pre-Tool-Use Hook (Node.js)
|
|
4
|
+
* Enforces delegation by warning when orchestrator attempts direct source file edits
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as path from 'path';
|
|
8
|
+
|
|
9
|
+
// Allowed path patterns (no warning)
|
|
10
|
+
const ALLOWED_PATH_PATTERNS = [
|
|
11
|
+
/\.omc\//,
|
|
12
|
+
/\.claude\//,
|
|
13
|
+
/\/\.claude\//,
|
|
14
|
+
/CLAUDE\.md$/,
|
|
15
|
+
/AGENTS\.md$/,
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
// Source file extensions (should warn)
|
|
19
|
+
const SOURCE_EXTENSIONS = new Set([
|
|
20
|
+
'.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs',
|
|
21
|
+
'.py', '.pyw',
|
|
22
|
+
'.go', '.rs', '.java', '.kt', '.scala',
|
|
23
|
+
'.c', '.cpp', '.cc', '.h', '.hpp',
|
|
24
|
+
'.rb', '.php',
|
|
25
|
+
'.svelte', '.vue',
|
|
26
|
+
'.graphql', '.gql',
|
|
27
|
+
'.sh', '.bash', '.zsh',
|
|
28
|
+
]);
|
|
29
|
+
|
|
30
|
+
function isAllowedPath(filePath) {
|
|
31
|
+
if (!filePath) return true;
|
|
32
|
+
return ALLOWED_PATH_PATTERNS.some(pattern => pattern.test(filePath));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function isSourceFile(filePath) {
|
|
36
|
+
if (!filePath) return false;
|
|
37
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
38
|
+
return SOURCE_EXTENSIONS.has(ext);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Patterns that indicate file modification in bash commands
|
|
42
|
+
const FILE_MODIFY_PATTERNS = [
|
|
43
|
+
/sed\s+-i/,
|
|
44
|
+
/>\s*[^&]/,
|
|
45
|
+
/>>/,
|
|
46
|
+
/tee\s+/,
|
|
47
|
+
/cat\s+.*>\s*/,
|
|
48
|
+
/echo\s+.*>\s*/,
|
|
49
|
+
/printf\s+.*>\s*/,
|
|
50
|
+
];
|
|
51
|
+
|
|
52
|
+
// Source file pattern for command inspection
|
|
53
|
+
const SOURCE_EXT_PATTERN = /\.(ts|tsx|js|jsx|mjs|cjs|py|pyw|go|rs|java|kt|scala|c|cpp|cc|h|hpp|rb|php|svelte|vue|graphql|gql|sh|bash|zsh)/i;
|
|
54
|
+
|
|
55
|
+
function checkBashCommand(command) {
|
|
56
|
+
// Check if command might modify files
|
|
57
|
+
const mayModify = FILE_MODIFY_PATTERNS.some(pattern => pattern.test(command));
|
|
58
|
+
if (!mayModify) return null;
|
|
59
|
+
|
|
60
|
+
// Check if it might affect source files
|
|
61
|
+
if (SOURCE_EXT_PATTERN.test(command)) {
|
|
62
|
+
return `[DELEGATION NOTICE] Bash command may modify source files: ${command}
|
|
63
|
+
|
|
64
|
+
Recommended: Delegate to executor agent instead:
|
|
65
|
+
Task(subagent_type="oh-my-claudecode:executor", model="sonnet", prompt="...")
|
|
66
|
+
|
|
67
|
+
This is a soft warning. Operation will proceed.`;
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async function main() {
|
|
73
|
+
let input = '';
|
|
74
|
+
|
|
75
|
+
// Read stdin
|
|
76
|
+
for await (const chunk of process.stdin) {
|
|
77
|
+
input += chunk;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let data;
|
|
81
|
+
try {
|
|
82
|
+
data = JSON.parse(input);
|
|
83
|
+
} catch {
|
|
84
|
+
console.log(JSON.stringify({ continue: true }));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Extract tool name (handle both cases)
|
|
89
|
+
const toolName = data.tool_name || data.toolName || '';
|
|
90
|
+
|
|
91
|
+
// Handle Bash tool separately - check for file modification patterns
|
|
92
|
+
if (toolName === 'Bash' || toolName === 'bash') {
|
|
93
|
+
const toolInput = data.tool_input || data.toolInput || {};
|
|
94
|
+
const command = toolInput.command || '';
|
|
95
|
+
const warning = checkBashCommand(command);
|
|
96
|
+
if (warning) {
|
|
97
|
+
console.log(JSON.stringify({ continue: true, message: warning }));
|
|
98
|
+
} else {
|
|
99
|
+
console.log(JSON.stringify({ continue: true }));
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Only check Edit and Write tools
|
|
105
|
+
if (!['Edit', 'Write', 'edit', 'write'].includes(toolName)) {
|
|
106
|
+
console.log(JSON.stringify({ continue: true }));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Extract file path (handle nested structures)
|
|
111
|
+
const toolInput = data.tool_input || data.toolInput || {};
|
|
112
|
+
const filePath = toolInput.file_path || toolInput.filePath || '';
|
|
113
|
+
|
|
114
|
+
// No file path? Allow
|
|
115
|
+
if (!filePath) {
|
|
116
|
+
console.log(JSON.stringify({ continue: true }));
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Check if allowed path
|
|
121
|
+
if (isAllowedPath(filePath)) {
|
|
122
|
+
console.log(JSON.stringify({ continue: true }));
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if source file
|
|
127
|
+
if (isSourceFile(filePath)) {
|
|
128
|
+
const warning = `[DELEGATION NOTICE] Direct ${toolName} on source file: ${filePath}
|
|
129
|
+
|
|
130
|
+
Recommended: Delegate to executor agent instead:
|
|
131
|
+
Task(subagent_type="oh-my-claudecode:executor", model="sonnet", prompt="...")
|
|
132
|
+
|
|
133
|
+
This is a soft warning. Operation will proceed.`;
|
|
134
|
+
|
|
135
|
+
console.log(JSON.stringify({ continue: true, message: warning }));
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Not a source file, allow without warning
|
|
140
|
+
console.log(JSON.stringify({ continue: true }));
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
main().catch(() => {
|
|
144
|
+
console.log(JSON.stringify({ continue: true }));
|
|
145
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# OMC Pre-Tool-Use Hook
|
|
3
|
+
# Enforces delegation by warning when orchestrator attempts direct source file edits
|
|
4
|
+
|
|
5
|
+
# Read stdin (JSON input from Claude Code)
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
|
|
8
|
+
# Extract tool name and file path
|
|
9
|
+
TOOL_NAME=""
|
|
10
|
+
FILE_PATH=""
|
|
11
|
+
if command -v jq &> /dev/null; then
|
|
12
|
+
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // ""' 2>/dev/null)
|
|
13
|
+
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // .toolInput.file_path // .toolInput.filePath // ""' 2>/dev/null)
|
|
14
|
+
else
|
|
15
|
+
TOOL_NAME=$(echo "$INPUT" | grep -oP '"tool_name"\s*:\s*"\K[^"]+' | head -1)
|
|
16
|
+
if [ -z "$TOOL_NAME" ]; then
|
|
17
|
+
TOOL_NAME=$(echo "$INPUT" | grep -oP '"toolName"\s*:\s*"\K[^"]+' | head -1)
|
|
18
|
+
fi
|
|
19
|
+
FILE_PATH=$(echo "$INPUT" | grep -oP '"file_path"\s*:\s*"\K[^"]+' | head -1)
|
|
20
|
+
if [ -z "$FILE_PATH" ]; then
|
|
21
|
+
FILE_PATH=$(echo "$INPUT" | grep -oP '"filePath"\s*:\s*"\K[^"]+' | head -1)
|
|
22
|
+
fi
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
# Handle Bash tool separately - check for file modification patterns
|
|
26
|
+
if [ "$TOOL_NAME" = "Bash" ] || [ "$TOOL_NAME" = "bash" ]; then
|
|
27
|
+
# Extract command
|
|
28
|
+
COMMAND=""
|
|
29
|
+
if command -v jq &> /dev/null; then
|
|
30
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // .toolInput.command // ""' 2>/dev/null)
|
|
31
|
+
else
|
|
32
|
+
COMMAND=$(echo "$INPUT" | grep -oP '"command"\s*:\s*"\K[^"]+' | head -1)
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
# Check for file modification patterns
|
|
36
|
+
if echo "$COMMAND" | grep -qE '(sed\s+-i|>\s*[^&]|>>\s*|tee\s+|cat\s+.*>\s*|echo\s+.*>\s*|printf\s+.*>\s*)'; then
|
|
37
|
+
# Check if modifying source files
|
|
38
|
+
SOURCE_PATTERN='\.(ts|tsx|js|jsx|mjs|cjs|py|pyw|go|rs|java|kt|scala|c|cpp|cc|h|hpp|rb|php|svelte|vue|graphql|gql|sh|bash|zsh)'
|
|
39
|
+
if echo "$COMMAND" | grep -qE "$SOURCE_PATTERN"; then
|
|
40
|
+
# Might be modifying source files - warn
|
|
41
|
+
WARNING="[DELEGATION NOTICE] Bash command may modify source files: $COMMAND
|
|
42
|
+
|
|
43
|
+
Recommended: Delegate to executor agent instead:
|
|
44
|
+
Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
|
|
45
|
+
|
|
46
|
+
This is a soft warning. Operation will proceed."
|
|
47
|
+
WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs . 2>/dev/null || echo "\"$WARNING\"")
|
|
48
|
+
echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
fi
|
|
52
|
+
# Bash command is OK
|
|
53
|
+
echo '{"continue": true}'
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# Only check Edit and Write tools
|
|
58
|
+
if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ] && \
|
|
59
|
+
[ "$TOOL_NAME" != "edit" ] && [ "$TOOL_NAME" != "write" ]; then
|
|
60
|
+
echo '{"continue": true}'
|
|
61
|
+
exit 0
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# No file path? Allow
|
|
65
|
+
if [ -z "$FILE_PATH" ]; then
|
|
66
|
+
echo '{"continue": true}'
|
|
67
|
+
exit 0
|
|
68
|
+
fi
|
|
69
|
+
|
|
70
|
+
# Check allowed paths (always OK)
|
|
71
|
+
if [[ "$FILE_PATH" == *".omc/"* ]] || \
|
|
72
|
+
[[ "$FILE_PATH" == *".claude/"* ]] || \
|
|
73
|
+
[[ "$FILE_PATH" == *"/.claude/"* ]] || \
|
|
74
|
+
[[ "$FILE_PATH" == "CLAUDE.md" ]] || \
|
|
75
|
+
[[ "$FILE_PATH" == *"/CLAUDE.md" ]] || \
|
|
76
|
+
[[ "$FILE_PATH" == "AGENTS.md" ]] || \
|
|
77
|
+
[[ "$FILE_PATH" == *"/AGENTS.md" ]]; then
|
|
78
|
+
echo '{"continue": true}'
|
|
79
|
+
exit 0
|
|
80
|
+
fi
|
|
81
|
+
|
|
82
|
+
# Check if source file extension (should warn)
|
|
83
|
+
EXT="${FILE_PATH##*.}"
|
|
84
|
+
EXT_LOWER=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
|
|
85
|
+
|
|
86
|
+
SOURCE_EXTS="ts tsx js jsx mjs cjs py pyw go rs java kt scala c cpp cc h hpp rb php svelte vue graphql gql sh bash zsh"
|
|
87
|
+
|
|
88
|
+
IS_SOURCE=false
|
|
89
|
+
for src_ext in $SOURCE_EXTS; do
|
|
90
|
+
if [ "$EXT_LOWER" = "$src_ext" ]; then
|
|
91
|
+
IS_SOURCE=true
|
|
92
|
+
break
|
|
93
|
+
fi
|
|
94
|
+
done
|
|
95
|
+
|
|
96
|
+
if [ "$IS_SOURCE" = true ]; then
|
|
97
|
+
# Emit warning but allow (soft enforcement)
|
|
98
|
+
WARNING="[DELEGATION NOTICE] Direct $TOOL_NAME on source file: $FILE_PATH
|
|
99
|
+
|
|
100
|
+
Recommended: Delegate to executor agent instead:
|
|
101
|
+
Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
|
|
102
|
+
|
|
103
|
+
This is a soft warning. Operation will proceed."
|
|
104
|
+
|
|
105
|
+
# Escape for JSON
|
|
106
|
+
WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs .)
|
|
107
|
+
echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
|
|
108
|
+
exit 0
|
|
109
|
+
fi
|
|
110
|
+
|
|
111
|
+
# Not a source file, allow without warning
|
|
112
|
+
echo '{"continue": true}'
|
|
113
|
+
exit 0
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// OMC Session Start Hook (Node.js)
|
|
3
|
+
// Restores persistent mode states when session starts
|
|
4
|
+
// Cross-platform: Windows, macOS, Linux
|
|
5
|
+
|
|
6
|
+
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
|
|
10
|
+
async function readStdin() {
|
|
11
|
+
const chunks = [];
|
|
12
|
+
for await (const chunk of process.stdin) {
|
|
13
|
+
chunks.push(chunk);
|
|
14
|
+
}
|
|
15
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function readJsonFile(path) {
|
|
19
|
+
try {
|
|
20
|
+
if (!existsSync(path)) return null;
|
|
21
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
22
|
+
} catch {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function countIncompleteTodos(todosDir) {
|
|
28
|
+
let count = 0;
|
|
29
|
+
if (existsSync(todosDir)) {
|
|
30
|
+
try {
|
|
31
|
+
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
32
|
+
for (const file of files) {
|
|
33
|
+
const todos = readJsonFile(join(todosDir, file));
|
|
34
|
+
if (Array.isArray(todos)) {
|
|
35
|
+
count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
} catch {}
|
|
39
|
+
}
|
|
40
|
+
return count;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function main() {
|
|
44
|
+
try {
|
|
45
|
+
const input = await readStdin();
|
|
46
|
+
let data = {};
|
|
47
|
+
try { data = JSON.parse(input); } catch {}
|
|
48
|
+
|
|
49
|
+
const directory = data.directory || process.cwd();
|
|
50
|
+
const messages = [];
|
|
51
|
+
|
|
52
|
+
// Check for ultrawork state
|
|
53
|
+
const ultraworkState = readJsonFile(join(directory, '.omc', 'ultrawork-state.json'))
|
|
54
|
+
|| readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
|
|
55
|
+
|
|
56
|
+
if (ultraworkState?.active) {
|
|
57
|
+
messages.push(`<session-restore>
|
|
58
|
+
|
|
59
|
+
[ULTRAWORK MODE RESTORED]
|
|
60
|
+
|
|
61
|
+
You have an active ultrawork session from ${ultraworkState.started_at}.
|
|
62
|
+
Original task: ${ultraworkState.original_prompt}
|
|
63
|
+
|
|
64
|
+
Continue working in ultrawork mode until all tasks are complete.
|
|
65
|
+
|
|
66
|
+
</session-restore>
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Check for incomplete todos
|
|
73
|
+
const todosDir = join(homedir(), '.claude', 'todos');
|
|
74
|
+
const incompleteCount = countIncompleteTodos(todosDir);
|
|
75
|
+
|
|
76
|
+
if (incompleteCount > 0) {
|
|
77
|
+
messages.push(`<session-restore>
|
|
78
|
+
|
|
79
|
+
[PENDING TASKS DETECTED]
|
|
80
|
+
|
|
81
|
+
You have ${incompleteCount} incomplete tasks from a previous session.
|
|
82
|
+
Please continue working on these tasks.
|
|
83
|
+
|
|
84
|
+
</session-restore>
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (messages.length > 0) {
|
|
91
|
+
console.log(JSON.stringify({ continue: true, message: messages.join('\n') }));
|
|
92
|
+
} else {
|
|
93
|
+
console.log(JSON.stringify({ continue: true }));
|
|
94
|
+
}
|
|
95
|
+
} catch (error) {
|
|
96
|
+
console.log(JSON.stringify({ continue: true }));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
main();
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# OMC Session Start Hook
|
|
3
|
+
# Restores persistent mode states and injects context when session starts
|
|
4
|
+
|
|
5
|
+
# Read stdin
|
|
6
|
+
INPUT=$(cat)
|
|
7
|
+
|
|
8
|
+
# Get directory
|
|
9
|
+
DIRECTORY=""
|
|
10
|
+
if command -v jq &> /dev/null; then
|
|
11
|
+
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
if [ -z "$DIRECTORY" ]; then
|
|
15
|
+
DIRECTORY=$(pwd)
|
|
16
|
+
fi
|
|
17
|
+
|
|
18
|
+
MESSAGES=""
|
|
19
|
+
|
|
20
|
+
# Check for active ultrawork state
|
|
21
|
+
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ] || [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
|
|
22
|
+
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
|
|
23
|
+
ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
24
|
+
else
|
|
25
|
+
ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
26
|
+
fi
|
|
27
|
+
|
|
28
|
+
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
29
|
+
if [ "$IS_ACTIVE" = "true" ]; then
|
|
30
|
+
STARTED_AT=$(echo "$ULTRAWORK_STATE" | jq -r '.started_at // ""' 2>/dev/null)
|
|
31
|
+
PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
32
|
+
MESSAGES="$MESSAGES<session-restore>\\n\\n[ULTRAWORK MODE RESTORED]\\n\\nYou have an active ultrawork session from $STARTED_AT.\\nOriginal task: $PROMPT\\n\\nContinue working in ultrawork mode until all tasks are complete.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
33
|
+
fi
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# Check for incomplete todos
|
|
37
|
+
INCOMPLETE_COUNT=0
|
|
38
|
+
TODOS_DIR="$HOME/.claude/todos"
|
|
39
|
+
if [ -d "$TODOS_DIR" ]; then
|
|
40
|
+
for todo_file in "$TODOS_DIR"/*.json; do
|
|
41
|
+
if [ -f "$todo_file" ]; then
|
|
42
|
+
if command -v jq &> /dev/null; then
|
|
43
|
+
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
44
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
45
|
+
fi
|
|
46
|
+
fi
|
|
47
|
+
done
|
|
48
|
+
fi
|
|
49
|
+
|
|
50
|
+
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
51
|
+
MESSAGES="$MESSAGES<session-restore>\\n\\n[PENDING TASKS DETECTED]\\n\\nYou have $INCOMPLETE_COUNT incomplete tasks from a previous session.\\nPlease continue working on these tasks.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
# Output message if we have any
|
|
55
|
+
if [ -n "$MESSAGES" ]; then
|
|
56
|
+
# Escape for JSON
|
|
57
|
+
MESSAGES_ESCAPED=$(echo "$MESSAGES" | sed 's/"/\\"/g')
|
|
58
|
+
echo "{\"continue\": true, \"message\": \"$MESSAGES_ESCAPED\"}"
|
|
59
|
+
else
|
|
60
|
+
echo '{"continue": true}'
|
|
61
|
+
fi
|
|
62
|
+
exit 0
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// OMC Stop Continuation Hook (Node.js)
|
|
3
|
+
// Checks for incomplete todos and injects continuation prompt
|
|
4
|
+
// Cross-platform: Windows, macOS, Linux
|
|
5
|
+
|
|
6
|
+
import { readdirSync, readFileSync, existsSync } from 'fs';
|
|
7
|
+
import { join } from 'path';
|
|
8
|
+
import { homedir } from 'os';
|
|
9
|
+
|
|
10
|
+
// Read all stdin
|
|
11
|
+
async function readStdin() {
|
|
12
|
+
const chunks = [];
|
|
13
|
+
for await (const chunk of process.stdin) {
|
|
14
|
+
chunks.push(chunk);
|
|
15
|
+
}
|
|
16
|
+
return Buffer.concat(chunks).toString('utf-8');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Main
|
|
20
|
+
async function main() {
|
|
21
|
+
try {
|
|
22
|
+
// Read stdin (we don't use it much, but need to consume it)
|
|
23
|
+
await readStdin();
|
|
24
|
+
|
|
25
|
+
// Check for incomplete todos
|
|
26
|
+
const todosDir = join(homedir(), '.claude', 'todos');
|
|
27
|
+
|
|
28
|
+
if (!existsSync(todosDir)) {
|
|
29
|
+
console.log(JSON.stringify({ continue: true }));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
let incompleteCount = 0;
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
|
|
37
|
+
|
|
38
|
+
for (const file of files) {
|
|
39
|
+
try {
|
|
40
|
+
const content = readFileSync(join(todosDir, file), 'utf-8');
|
|
41
|
+
const todos = JSON.parse(content);
|
|
42
|
+
|
|
43
|
+
if (Array.isArray(todos)) {
|
|
44
|
+
const incomplete = todos.filter(
|
|
45
|
+
t => t.status !== 'completed' && t.status !== 'cancelled'
|
|
46
|
+
);
|
|
47
|
+
incompleteCount += incomplete.length;
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
// Skip files that can't be parsed
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
} catch {
|
|
54
|
+
// Directory read error - allow continuation
|
|
55
|
+
console.log(JSON.stringify({ continue: true }));
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (incompleteCount > 0) {
|
|
60
|
+
const reason = `[SYSTEM REMINDER - TODO CONTINUATION]
|
|
61
|
+
|
|
62
|
+
Incomplete tasks remain in your todo list (${incompleteCount} remaining). Continue working on the next pending task.
|
|
63
|
+
|
|
64
|
+
- Proceed without asking for permission
|
|
65
|
+
- Mark each task complete when finished
|
|
66
|
+
- Do not stop until all tasks are done`;
|
|
67
|
+
|
|
68
|
+
console.log(JSON.stringify({ continue: false, reason }));
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// No incomplete todos - allow stop
|
|
73
|
+
console.log(JSON.stringify({ continue: true }));
|
|
74
|
+
} catch (error) {
|
|
75
|
+
// On any error, allow continuation
|
|
76
|
+
console.log(JSON.stringify({ continue: true }));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
main();
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# OMC Stop Continuation Hook
|
|
3
|
+
# Checks for incomplete todos and injects continuation prompt
|
|
4
|
+
# Ported from oh-my-opencode's todo-continuation-enforcer
|
|
5
|
+
|
|
6
|
+
# Read stdin
|
|
7
|
+
INPUT=$(cat)
|
|
8
|
+
|
|
9
|
+
# Get session ID if available
|
|
10
|
+
SESSION_ID=""
|
|
11
|
+
if command -v jq &> /dev/null; then
|
|
12
|
+
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
# Check for incomplete todos in the Claude todos directory
|
|
16
|
+
TODOS_DIR="$HOME/.claude/todos"
|
|
17
|
+
if [ -d "$TODOS_DIR" ]; then
|
|
18
|
+
# Look for any todo files with incomplete items
|
|
19
|
+
INCOMPLETE_COUNT=0
|
|
20
|
+
for todo_file in "$TODOS_DIR"/*.json; do
|
|
21
|
+
if [ -f "$todo_file" ]; then
|
|
22
|
+
if command -v jq &> /dev/null; then
|
|
23
|
+
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
24
|
+
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
25
|
+
fi
|
|
26
|
+
fi
|
|
27
|
+
done
|
|
28
|
+
|
|
29
|
+
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
30
|
+
# Output continuation message
|
|
31
|
+
cat << EOF
|
|
32
|
+
{"continue": false, "reason": "[SYSTEM REMINDER - TODO CONTINUATION]\\n\\nIncomplete tasks remain in your todo list ($INCOMPLETE_COUNT remaining). Continue working on the next pending task.\\n\\n- Proceed without asking for permission\\n- Mark each task complete when finished\\n- Do not stop until all tasks are done"}
|
|
33
|
+
EOF
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# No incomplete todos - allow stop
|
|
39
|
+
echo '{"continue": true}'
|
|
40
|
+
exit 0
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Rules Templates
|
|
2
|
+
|
|
3
|
+
This directory contains rule templates that you can copy to your project's `.claude/rules/` directory.
|
|
4
|
+
|
|
5
|
+
## How to Use
|
|
6
|
+
|
|
7
|
+
1. Create a `.claude/rules/` directory in your project root
|
|
8
|
+
2. Copy the templates you want to use
|
|
9
|
+
3. Customize them for your project
|
|
10
|
+
4. Rules in `.claude/rules/*.md` will be auto-discovered and injected into context
|
|
11
|
+
|
|
12
|
+
## Available Templates
|
|
13
|
+
|
|
14
|
+
| Template | Purpose |
|
|
15
|
+
|----------|---------|
|
|
16
|
+
| `coding-style.md` | Code style and formatting guidelines |
|
|
17
|
+
| `testing.md` | Testing requirements and coverage targets |
|
|
18
|
+
| `security.md` | Security checklist and best practices |
|
|
19
|
+
| `performance.md` | Performance guidelines and model selection |
|
|
20
|
+
| `git-workflow.md` | Git commit and PR workflow |
|
|
21
|
+
|
|
22
|
+
## Auto-Discovery
|
|
23
|
+
|
|
24
|
+
When you place rules in `.claude/rules/`, they are automatically discovered by oh-my-claudecode and injected into the context for all agents working in your project.
|
|
25
|
+
|
|
26
|
+
## Example
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# Copy templates to your project
|
|
30
|
+
mkdir -p .claude/rules
|
|
31
|
+
cp templates/rules/security.md .claude/rules/
|
|
32
|
+
cp templates/rules/testing.md .claude/rules/
|
|
33
|
+
|
|
34
|
+
# Customize for your project
|
|
35
|
+
# Edit .claude/rules/security.md to add project-specific checks
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Customization
|
|
39
|
+
|
|
40
|
+
Each template has `[CUSTOMIZE]` markers where you should add project-specific guidelines.
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Coding Style Rules
|
|
2
|
+
|
|
3
|
+
## Immutability (CRITICAL)
|
|
4
|
+
|
|
5
|
+
ALWAYS create new objects, NEVER mutate:
|
|
6
|
+
|
|
7
|
+
```javascript
|
|
8
|
+
// WRONG: Mutation
|
|
9
|
+
function updateUser(user, name) {
|
|
10
|
+
user.name = name // MUTATION!
|
|
11
|
+
return user
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// CORRECT: Immutability
|
|
15
|
+
function updateUser(user, name) {
|
|
16
|
+
return { ...user, name }
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## File Organization
|
|
21
|
+
|
|
22
|
+
MANY SMALL FILES > FEW LARGE FILES:
|
|
23
|
+
- High cohesion, low coupling
|
|
24
|
+
- 200-400 lines typical, 800 max
|
|
25
|
+
- Extract utilities from large components
|
|
26
|
+
- Organize by feature/domain, not by type
|
|
27
|
+
|
|
28
|
+
## Error Handling
|
|
29
|
+
|
|
30
|
+
ALWAYS handle errors comprehensively:
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
try {
|
|
34
|
+
const result = await riskyOperation()
|
|
35
|
+
return result
|
|
36
|
+
} catch (error) {
|
|
37
|
+
console.error('Operation failed:', error)
|
|
38
|
+
throw new Error('User-friendly error message')
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Input Validation
|
|
43
|
+
|
|
44
|
+
ALWAYS validate user input:
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { z } from 'zod'
|
|
48
|
+
|
|
49
|
+
const schema = z.object({
|
|
50
|
+
email: z.string().email(),
|
|
51
|
+
age: z.number().int().min(0).max(150)
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const validated = schema.parse(input)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Code Quality Checklist
|
|
58
|
+
|
|
59
|
+
Before marking work complete:
|
|
60
|
+
- [ ] Code is readable and well-named
|
|
61
|
+
- [ ] Functions are small (<50 lines)
|
|
62
|
+
- [ ] Files are focused (<800 lines)
|
|
63
|
+
- [ ] No deep nesting (>4 levels)
|
|
64
|
+
- [ ] Proper error handling
|
|
65
|
+
- [ ] No console.log statements
|
|
66
|
+
- [ ] No hardcoded values
|
|
67
|
+
- [ ] Immutable patterns used
|
|
68
|
+
|
|
69
|
+
## [CUSTOMIZE] Project-Specific Style
|
|
70
|
+
|
|
71
|
+
Add your project-specific coding style rules here:
|
|
72
|
+
- Naming conventions
|
|
73
|
+
- File structure requirements
|
|
74
|
+
- Framework-specific patterns
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Git Workflow Rules
|
|
2
|
+
|
|
3
|
+
## Commit Message Format
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
<type>: <description>
|
|
7
|
+
|
|
8
|
+
<optional body>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Types: feat, fix, refactor, docs, test, chore, perf, ci
|
|
12
|
+
|
|
13
|
+
## Pull Request Workflow
|
|
14
|
+
|
|
15
|
+
When creating PRs:
|
|
16
|
+
1. Analyze full commit history (not just latest commit)
|
|
17
|
+
2. Use `git diff [base-branch]...HEAD` to see all changes
|
|
18
|
+
3. Draft comprehensive PR summary
|
|
19
|
+
4. Include test plan with TODOs
|
|
20
|
+
5. Push with `-u` flag if new branch
|
|
21
|
+
|
|
22
|
+
## Feature Implementation Workflow
|
|
23
|
+
|
|
24
|
+
1. **Plan First** - Use `planner` agent
|
|
25
|
+
2. **TDD Approach** - Use `tdd-guide` agent
|
|
26
|
+
3. **Code Review** - Use `code-reviewer` agent after writing code
|
|
27
|
+
4. **Commit** - Follow conventional commits format
|
|
28
|
+
|
|
29
|
+
## Branch Naming
|
|
30
|
+
|
|
31
|
+
- `feature/` - New features
|
|
32
|
+
- `fix/` - Bug fixes
|
|
33
|
+
- `refactor/` - Code refactoring
|
|
34
|
+
- `docs/` - Documentation changes
|
|
35
|
+
|
|
36
|
+
## [CUSTOMIZE] Project-Specific Git Rules
|
|
37
|
+
|
|
38
|
+
Add your project-specific git workflow here:
|
|
39
|
+
- Branch protection rules
|
|
40
|
+
- Required reviewers
|
|
41
|
+
- CI/CD requirements
|