cortexhawk 3.3.0 → 3.3.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/CHANGELOG.md +21 -1
- package/README.md +25 -7
- package/commands/cleanup.md +1 -0
- package/cortexhawk +7 -1
- package/hooks/branch-guard.sh +1 -2
- package/hooks/codex-dispatcher.sh +3 -0
- package/install.sh +55 -934
- package/mcp/context7.json +1 -1
- package/mcp/github.json +1 -1
- package/mcp/puppeteer.json +1 -1
- package/mcp/sequential-thinking.json +1 -1
- package/package.json +1 -1
- package/scripts/doctor.sh +164 -0
- package/scripts/install-claude.sh +179 -0
- package/scripts/post-merge-cleanup.sh +170 -80
- package/scripts/restore.sh +212 -0
- package/scripts/snapshot.sh +163 -0
- package/scripts/update.sh +280 -0
- package/templates/AGENT.md +19 -0
- package/templates/CLAUDE.md.template +41 -0
- package/templates/COMMAND.md +14 -0
- package/templates/ORCHESTRATION.md +79 -0
- package/templates/PERSONA.md +17 -0
- package/templates/SKILL.md +17 -0
- package/templates/github/PULL_REQUEST_TEMPLATE.md +26 -0
- package/templates/github/gitmessage +10 -0
package/mcp/context7.json
CHANGED
package/mcp/github.json
CHANGED
package/mcp/puppeteer.json
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# doctor.sh — CortexHawk installation health diagnostics
|
|
3
|
+
# Sourced by install.sh when --doctor is used
|
|
4
|
+
# Uses shared functions: get_version, green, yellow
|
|
5
|
+
# Uses globals: GLOBAL, TARGET, SCRIPT_DIR
|
|
6
|
+
|
|
7
|
+
do_doctor() {
|
|
8
|
+
if [ "$GLOBAL" = true ]; then
|
|
9
|
+
TARGET="$HOME/.claude"
|
|
10
|
+
else
|
|
11
|
+
TARGET="$(pwd)/.claude"
|
|
12
|
+
fi
|
|
13
|
+
|
|
14
|
+
local ok=0 warn=0 err=0
|
|
15
|
+
_doc_ok() { echo " [OK] $1"; ok=$((ok+1)); }
|
|
16
|
+
_doc_warn() { echo " [WARN] $1"; warn=$((warn+1)); }
|
|
17
|
+
_doc_err() { echo " [ERR] $1"; err=$((err+1)); }
|
|
18
|
+
|
|
19
|
+
# Header
|
|
20
|
+
local version="" profile="" target_cli_name=""
|
|
21
|
+
if [ -f "$TARGET/.cortexhawk-manifest" ]; then
|
|
22
|
+
version=$(grep -o '"version": "[^"]*"' "$TARGET/.cortexhawk-manifest" | head -1 | cut -d'"' -f4)
|
|
23
|
+
profile=$(grep -o '"profile": "[^"]*"' "$TARGET/.cortexhawk-manifest" | head -1 | cut -d'"' -f4)
|
|
24
|
+
target_cli_name=$(grep -o '"target": "[^"]*"' "$TARGET/.cortexhawk-manifest" | head -1 | cut -d'"' -f4)
|
|
25
|
+
fi
|
|
26
|
+
echo "CortexHawk Doctor"
|
|
27
|
+
echo "==================="
|
|
28
|
+
echo " Installation: $TARGET"
|
|
29
|
+
echo " Version: ${version:-unknown}"
|
|
30
|
+
echo " Profile: ${profile:-unknown}"
|
|
31
|
+
echo " Target: ${target_cli_name:-claude}"
|
|
32
|
+
echo ""
|
|
33
|
+
echo "Checks:"
|
|
34
|
+
|
|
35
|
+
# 1. Manifest
|
|
36
|
+
if [ -f "$TARGET/.cortexhawk-manifest" ]; then
|
|
37
|
+
_doc_ok "Manifest present"
|
|
38
|
+
else
|
|
39
|
+
_doc_err "Manifest missing — run install.sh to create an installation"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
# 2. settings.json valid JSON
|
|
43
|
+
if [ -f "$TARGET/settings.json" ]; then
|
|
44
|
+
if command -v python3 >/dev/null 2>&1; then
|
|
45
|
+
if python3 -c "import json,sys; json.load(open(sys.argv[1]))" "$TARGET/settings.json" 2>/dev/null; then
|
|
46
|
+
_doc_ok "settings.json valid JSON"
|
|
47
|
+
else
|
|
48
|
+
_doc_err "settings.json invalid JSON"
|
|
49
|
+
fi
|
|
50
|
+
else
|
|
51
|
+
_doc_warn "settings.json present (python3 not available for validation)"
|
|
52
|
+
fi
|
|
53
|
+
else
|
|
54
|
+
_doc_warn "settings.json not found"
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# 3. Component counts (compare installed vs source)
|
|
58
|
+
for comp in agents commands modes; do
|
|
59
|
+
local installed=0 source_count=0
|
|
60
|
+
installed=$(find "$TARGET/$comp" -name "*.md" -type f 2>/dev/null | wc -l)
|
|
61
|
+
source_count=$(find "$SCRIPT_DIR/$comp" -name "*.md" -type f 2>/dev/null | wc -l)
|
|
62
|
+
if [ "$installed" -eq "$source_count" ] 2>/dev/null; then
|
|
63
|
+
_doc_ok "$installed/$source_count $comp installed"
|
|
64
|
+
elif [ "$installed" -gt 0 ] 2>/dev/null; then
|
|
65
|
+
_doc_warn "$installed/$source_count $comp installed"
|
|
66
|
+
else
|
|
67
|
+
_doc_err "0/$source_count $comp installed"
|
|
68
|
+
fi
|
|
69
|
+
done
|
|
70
|
+
|
|
71
|
+
# 4. Skills (profile-dependent, just count what's there)
|
|
72
|
+
local skills_installed=0 skills_source=0
|
|
73
|
+
skills_installed=$(find "$TARGET/skills" -name "*.md" -type f 2>/dev/null | wc -l)
|
|
74
|
+
skills_source=$(find "$SCRIPT_DIR/skills" -name "*.md" -type f 2>/dev/null | wc -l)
|
|
75
|
+
if [ "$skills_installed" -gt 0 ] 2>/dev/null; then
|
|
76
|
+
_doc_ok "$skills_installed/$skills_source skills installed (profile: ${profile:-all})"
|
|
77
|
+
else
|
|
78
|
+
_doc_err "No skills installed"
|
|
79
|
+
fi
|
|
80
|
+
|
|
81
|
+
# 5. Hooks executable
|
|
82
|
+
local hooks_ok=0 hooks_total=0
|
|
83
|
+
for hook in "$TARGET/hooks/"*.sh; do
|
|
84
|
+
[ -f "$hook" ] || continue
|
|
85
|
+
hooks_total=$((hooks_total+1))
|
|
86
|
+
if [ -x "$hook" ]; then
|
|
87
|
+
hooks_ok=$((hooks_ok+1))
|
|
88
|
+
else
|
|
89
|
+
_doc_warn "Hook not executable: $(basename "$hook")"
|
|
90
|
+
fi
|
|
91
|
+
done
|
|
92
|
+
if [ "$hooks_total" -gt 0 ]; then
|
|
93
|
+
if [ "$hooks_ok" -eq "$hooks_total" ]; then
|
|
94
|
+
_doc_ok "$hooks_ok/$hooks_total hooks executable"
|
|
95
|
+
fi
|
|
96
|
+
else
|
|
97
|
+
_doc_warn "No hooks found"
|
|
98
|
+
fi
|
|
99
|
+
|
|
100
|
+
# 6. compose.yml vs settings.json coherence
|
|
101
|
+
if [ -f "$TARGET/../hooks/compose.yml" ] || [ -f "$SCRIPT_DIR/hooks/compose.yml" ]; then
|
|
102
|
+
_doc_ok "compose.yml present"
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
# 7. MCP configs
|
|
106
|
+
if [ -d "$TARGET/mcp" ] && [ "$(find "$TARGET/mcp" -type f 2>/dev/null | wc -l)" -gt 0 ]; then
|
|
107
|
+
_doc_ok "MCP configs present"
|
|
108
|
+
elif [ -d "$TARGET/mcp" ]; then
|
|
109
|
+
_doc_warn "MCP directory exists but empty"
|
|
110
|
+
fi
|
|
111
|
+
|
|
112
|
+
# 8. docs/ workspace
|
|
113
|
+
local project_root
|
|
114
|
+
project_root="$(dirname "$TARGET")"
|
|
115
|
+
if [ -d "$project_root/docs" ]; then
|
|
116
|
+
_doc_ok "docs/ workspace exists"
|
|
117
|
+
else
|
|
118
|
+
_doc_warn "docs/ workspace missing"
|
|
119
|
+
fi
|
|
120
|
+
|
|
121
|
+
# 9. Broken symlinks in docs/plans/
|
|
122
|
+
local broken=0
|
|
123
|
+
if [ -d "$project_root/docs/plans" ]; then
|
|
124
|
+
while IFS= read -r link; do
|
|
125
|
+
[ -z "$link" ] && continue
|
|
126
|
+
_doc_warn "Broken symlink: $link"
|
|
127
|
+
broken=$((broken+1))
|
|
128
|
+
done < <(find "$project_root/docs/plans" -type l ! -exec test -e {} \; -print 2>/dev/null)
|
|
129
|
+
[ "$broken" -eq 0 ] && _doc_ok "No broken symlinks in docs/plans/"
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# 10. git-workflow.conf
|
|
133
|
+
if [ -f "$TARGET/git-workflow.conf" ]; then
|
|
134
|
+
_doc_ok "git-workflow.conf present"
|
|
135
|
+
else
|
|
136
|
+
_doc_warn "git-workflow.conf not found (run --init to configure)"
|
|
137
|
+
fi
|
|
138
|
+
|
|
139
|
+
# 11. CLAUDE.md at project root
|
|
140
|
+
if [ -f "$project_root/CLAUDE.md" ]; then
|
|
141
|
+
_doc_ok "CLAUDE.md present at project root"
|
|
142
|
+
else
|
|
143
|
+
_doc_warn "CLAUDE.md not found at project root"
|
|
144
|
+
fi
|
|
145
|
+
|
|
146
|
+
# 12. Version match source vs manifest
|
|
147
|
+
if [ -n "$version" ]; then
|
|
148
|
+
local source_version
|
|
149
|
+
source_version=$(get_version)
|
|
150
|
+
if [ "$version" = "$source_version" ]; then
|
|
151
|
+
_doc_ok "Version match: source $source_version = manifest $version"
|
|
152
|
+
else
|
|
153
|
+
_doc_warn "Version mismatch: source $source_version != manifest $version (run --update)"
|
|
154
|
+
fi
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# Summary
|
|
158
|
+
echo ""
|
|
159
|
+
echo "Summary: $ok OK, $warn WARN, $err ERR"
|
|
160
|
+
|
|
161
|
+
# Exit code: 1 if any errors
|
|
162
|
+
[ "$err" -gt 0 ] && exit 1
|
|
163
|
+
return 0
|
|
164
|
+
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# install-claude.sh — CortexHawk installer for Claude Code
|
|
3
|
+
# Sourced by install.sh for fresh Claude Code installations
|
|
4
|
+
# Uses shared functions: copy_all_components, count_component_files, generate_hooks_config,
|
|
5
|
+
# write_manifest, create_docs_workspace, run_audit, update_gitignore, setup_templates,
|
|
6
|
+
# install_git_post_merge_hook, do_quickstart
|
|
7
|
+
|
|
8
|
+
install_claude() {
|
|
9
|
+
if [ "$GLOBAL" = true ]; then
|
|
10
|
+
TARGET="$HOME/.claude"
|
|
11
|
+
else
|
|
12
|
+
TARGET="$(pwd)/.claude"
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
if [ "$DRY_RUN" = true ]; then
|
|
16
|
+
echo "CortexHawk Dry Run (install)"
|
|
17
|
+
echo "=============================="
|
|
18
|
+
echo " Target: $TARGET"
|
|
19
|
+
echo " Profile: ${PROFILE:-all}"
|
|
20
|
+
echo ""
|
|
21
|
+
echo "Would install:"
|
|
22
|
+
count_component_files "$SCRIPT_DIR"
|
|
23
|
+
echo " settings.json"
|
|
24
|
+
[ ! -f "$(pwd)/CLAUDE.md" ] && echo " CLAUDE.md"
|
|
25
|
+
echo ""
|
|
26
|
+
echo "No files were modified (dry run)."
|
|
27
|
+
return
|
|
28
|
+
fi
|
|
29
|
+
|
|
30
|
+
echo "Installing for Claude Code to project: $TARGET"
|
|
31
|
+
|
|
32
|
+
copy_all_components "$SCRIPT_DIR" "$TARGET" "$PROFILE"
|
|
33
|
+
|
|
34
|
+
# Copy agent personas from project root if present
|
|
35
|
+
local project_root
|
|
36
|
+
project_root="$(dirname "$TARGET")"
|
|
37
|
+
if [ -d "$project_root/.cortexhawk-agents" ]; then
|
|
38
|
+
cp -r "$project_root/.cortexhawk-agents/"*.md "$TARGET/agents/" 2>/dev/null || true
|
|
39
|
+
local persona_count
|
|
40
|
+
persona_count=$(find "$project_root/.cortexhawk-agents" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
|
|
41
|
+
[ "$persona_count" -gt 0 ] && echo " Loaded $persona_count agent persona(s) from .cortexhawk-agents/"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
local hooks_json
|
|
45
|
+
hooks_json=$(generate_hooks_config "$SCRIPT_DIR/hooks/compose.yml" ".claude/hooks")
|
|
46
|
+
if [ ! -f "$TARGET/settings.json" ]; then
|
|
47
|
+
# Fresh install: generate settings.json from scratch
|
|
48
|
+
if [ -n "$hooks_json" ] && [ "$hooks_json" != "{}" ]; then
|
|
49
|
+
echo "$hooks_json" | python3 -c "
|
|
50
|
+
import json, sys
|
|
51
|
+
hooks = json.load(sys.stdin)
|
|
52
|
+
with open(sys.argv[1]) as f:
|
|
53
|
+
permissions = json.load(f).get('permissions', {})
|
|
54
|
+
with open(sys.argv[2], 'w') as f:
|
|
55
|
+
json.dump({'permissions': permissions, 'hooks': hooks}, f, indent=2)
|
|
56
|
+
f.write('\n')
|
|
57
|
+
" "$SCRIPT_DIR/settings.json" "$TARGET/settings.json"
|
|
58
|
+
echo " Generated settings.json from hooks/compose.yml"
|
|
59
|
+
else
|
|
60
|
+
cp "$SCRIPT_DIR/settings.json" "$TARGET/settings.json"
|
|
61
|
+
fi
|
|
62
|
+
else
|
|
63
|
+
# Merge: preserve user customizations, add new hooks + permissions
|
|
64
|
+
if ! command -v python3 >/dev/null 2>&1; then
|
|
65
|
+
echo " Warning: python3 not found — copying settings.json (merge skipped)"
|
|
66
|
+
cp "$SCRIPT_DIR/settings.json" "$TARGET/settings.json"
|
|
67
|
+
else
|
|
68
|
+
python3 -c "
|
|
69
|
+
import json, sys, shutil, os
|
|
70
|
+
|
|
71
|
+
raw = sys.stdin.read().strip()
|
|
72
|
+
hooks = json.loads(raw) if raw else {}
|
|
73
|
+
|
|
74
|
+
# Load current settings (tolerate corrupted JSON)
|
|
75
|
+
try:
|
|
76
|
+
with open(sys.argv[1]) as f:
|
|
77
|
+
current = json.load(f)
|
|
78
|
+
except Exception:
|
|
79
|
+
backup = sys.argv[1] + '.bak'
|
|
80
|
+
if os.path.isfile(sys.argv[1]):
|
|
81
|
+
shutil.copy2(sys.argv[1], backup)
|
|
82
|
+
print(f' Warning: settings.json corrupted — backed up to {os.path.basename(backup)}', file=sys.stderr)
|
|
83
|
+
current = {}
|
|
84
|
+
|
|
85
|
+
try:
|
|
86
|
+
with open(sys.argv[2]) as f:
|
|
87
|
+
source = json.load(f)
|
|
88
|
+
except Exception:
|
|
89
|
+
source = {}
|
|
90
|
+
|
|
91
|
+
changes = []
|
|
92
|
+
|
|
93
|
+
# Merge hooks (regenerate from compose.yml)
|
|
94
|
+
if hooks and hooks != {}:
|
|
95
|
+
current['hooks'] = hooks
|
|
96
|
+
changes.append('hooks regenerated')
|
|
97
|
+
|
|
98
|
+
# Merge permissions (union: keep user additions + add new from source)
|
|
99
|
+
src_perms = source.get('permissions', {})
|
|
100
|
+
cur_perms = current.get('permissions', {})
|
|
101
|
+
for key in ('allow', 'deny'):
|
|
102
|
+
src_list = src_perms.get(key, [])
|
|
103
|
+
cur_list = cur_perms.get(key, [])
|
|
104
|
+
added = [p for p in src_list if p not in cur_list]
|
|
105
|
+
if added:
|
|
106
|
+
cur_list.extend(added)
|
|
107
|
+
changes.append(f'{len(added)} new {key} permission(s)')
|
|
108
|
+
cur_perms[key] = cur_list
|
|
109
|
+
current['permissions'] = cur_perms
|
|
110
|
+
|
|
111
|
+
with open(sys.argv[1], 'w') as f:
|
|
112
|
+
json.dump(current, f, indent=2)
|
|
113
|
+
f.write('\n')
|
|
114
|
+
|
|
115
|
+
if changes:
|
|
116
|
+
print(' Merged settings.json: ' + ', '.join(changes))
|
|
117
|
+
else:
|
|
118
|
+
print(' settings.json up to date — no merge needed')
|
|
119
|
+
" "$TARGET/settings.json" "$SCRIPT_DIR/settings.json" <<< "${hooks_json:-}"
|
|
120
|
+
fi
|
|
121
|
+
fi
|
|
122
|
+
|
|
123
|
+
PROJECT_ROOT="$(dirname "$TARGET")"
|
|
124
|
+
if [ ! -f "$PROJECT_ROOT/CLAUDE.md" ]; then
|
|
125
|
+
cp "$SCRIPT_DIR/CLAUDE.md" "$PROJECT_ROOT/CLAUDE.md"
|
|
126
|
+
else
|
|
127
|
+
echo "CLAUDE.md already exists — skipping"
|
|
128
|
+
fi
|
|
129
|
+
|
|
130
|
+
# Git workflow config (interactive in --init, defaults otherwise)
|
|
131
|
+
if [ "$GLOBAL" = false ]; then
|
|
132
|
+
if [ "$INIT_MODE" = true ]; then
|
|
133
|
+
source "$SCRIPT_DIR/scripts/git-workflow-init.sh" "$PROJECT_ROOT" "$TARGET"
|
|
134
|
+
elif [ ! -f "$TARGET/git-workflow.conf" ]; then
|
|
135
|
+
# Apply sensible defaults without asking
|
|
136
|
+
GIT_BRANCHING="direct-main"
|
|
137
|
+
GIT_COMMIT_CONVENTION="conventional"
|
|
138
|
+
GIT_PR_PREFERENCE="on-demand"
|
|
139
|
+
GIT_AUTO_PUSH="after-commit"
|
|
140
|
+
GIT_WORK_BRANCH=""
|
|
141
|
+
source "$SCRIPT_DIR/scripts/git-workflow-init.sh" "$PROJECT_ROOT" "$TARGET"
|
|
142
|
+
fi
|
|
143
|
+
fi
|
|
144
|
+
|
|
145
|
+
chmod +x "$TARGET/hooks/"*.sh 2>/dev/null || true
|
|
146
|
+
|
|
147
|
+
# Write manifest for future updates
|
|
148
|
+
write_manifest "$TARGET" "$PROFILE" "claude" false
|
|
149
|
+
|
|
150
|
+
# Create docs/ workspace for agent outputs (local only)
|
|
151
|
+
if [ "$GLOBAL" = false ]; then
|
|
152
|
+
create_docs_workspace "$(dirname "$TARGET")"
|
|
153
|
+
fi
|
|
154
|
+
|
|
155
|
+
run_audit "$(dirname "$TARGET")"
|
|
156
|
+
update_gitignore "$(dirname "$TARGET")" ".claude"
|
|
157
|
+
setup_templates "$(dirname "$TARGET")"
|
|
158
|
+
|
|
159
|
+
# Offer native git post-merge hook (opt-in, local only, interactive only)
|
|
160
|
+
if [ "$GLOBAL" = false ] && [ -d "$(pwd)/.git" ]; then
|
|
161
|
+
if [ -t 0 ]; then
|
|
162
|
+
echo ""
|
|
163
|
+
read -r -p "Install native git post-merge hook (auto-cleanup after merges)? [y/N]: " _hook_confirm
|
|
164
|
+
case "$_hook_confirm" in
|
|
165
|
+
[yY]*) install_git_post_merge_hook "$(pwd)" ;;
|
|
166
|
+
*) echo " → post-merge hook skipped (run: cortexhawk install --post-merge-hook to add later)" ;;
|
|
167
|
+
esac
|
|
168
|
+
fi
|
|
169
|
+
fi
|
|
170
|
+
|
|
171
|
+
echo ""
|
|
172
|
+
echo "CortexHawk installed successfully for Claude Code!"
|
|
173
|
+
echo ""
|
|
174
|
+
echo " 35 commands | 20 agents | 36 skills | 11 hooks | 7 modes"
|
|
175
|
+
echo ""
|
|
176
|
+
do_quickstart
|
|
177
|
+
echo ""
|
|
178
|
+
echo " To activate: exit Claude Code (ctrl+c) and relaunch 'claude' in this directory."
|
|
179
|
+
}
|