cortexhawk 3.2.0 → 3.3.1

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.
@@ -0,0 +1,280 @@
1
+ #!/bin/bash
2
+ # update.sh — CortexHawk update flow
3
+ # Sourced by install.sh when --update is used
4
+ # Uses shared functions: detect_source_type, get_version, do_snapshot, update_source_git,
5
+ # update_source_release, compute_checksum, sync_all_components, generate_hooks_config,
6
+ # write_manifest, run_audit, update_gitignore, setup_templates, cleanup_update
7
+
8
+ do_update() {
9
+ # 1. Validate target
10
+ if [ "$TARGET_CLI" != "claude" ]; then
11
+ echo "Error: --update is currently supported for Claude Code only"
12
+ echo "For Kimi CLI, re-run: install.sh --target kimi"
13
+ exit 1
14
+ fi
15
+
16
+ if [ "$GLOBAL" = true ]; then
17
+ TARGET="$HOME/.claude"
18
+ else
19
+ TARGET="$(pwd)/.claude"
20
+ fi
21
+
22
+ if [ ! -d "$TARGET" ]; then
23
+ echo "Error: no CortexHawk installation found at $TARGET"
24
+ echo "Run install.sh without --update for a fresh install"
25
+ exit 1
26
+ fi
27
+
28
+ # 2. Read manifest
29
+ local manifest="$TARGET/.cortexhawk-manifest"
30
+ local current_version="unknown"
31
+ local current_profile="all"
32
+ local source_type
33
+ source_type=$(detect_source_type)
34
+
35
+ if [ -f "$manifest" ]; then
36
+ current_version=$(grep '"version"' "$manifest" | sed 's/.*: *"\([^"]*\)".*/\1/')
37
+ current_profile=$(grep '"profile"' "$manifest" | sed 's/.*: *"\([^"]*\)".*/\1/')
38
+ local manifest_source
39
+ manifest_source=$(grep '"source"' "$manifest" | head -1 | sed 's/.*: *"\([^"]*\)".*/\1/')
40
+ # Only trust manifest's "git" source if SCRIPT_DIR is actually a git repo
41
+ if [ -n "$manifest_source" ]; then
42
+ if [ "$manifest_source" = "git" ] && git -C "$SCRIPT_DIR" rev-parse --git-dir >/dev/null 2>&1; then
43
+ source_type="git"
44
+ elif [ "$manifest_source" != "git" ]; then
45
+ source_type="$manifest_source"
46
+ fi
47
+ fi
48
+ else
49
+ echo " No manifest found — treating as pre-update installation"
50
+ fi
51
+
52
+ # 3. Profile override
53
+ local update_profile="$current_profile"
54
+ if [ -n "$PROFILE" ]; then
55
+ update_profile="$PROFILE"
56
+ fi
57
+
58
+ if [ "$DRY_RUN" = true ]; then
59
+ echo "CortexHawk Dry Run (update)"
60
+ echo "============================"
61
+ else
62
+ echo "CortexHawk Update"
63
+ echo "==================="
64
+ fi
65
+ echo " Current version: $current_version"
66
+ echo " Profile: $update_profile"
67
+ echo " Source: $source_type"
68
+ echo ""
69
+
70
+ # 3b. Auto-snapshot before update (skip in dry-run)
71
+ if [ "$DRY_RUN" != true ] && [ -f "$manifest" ]; then
72
+ echo "Creating pre-update snapshot..."
73
+ do_snapshot || echo " Warning: pre-update snapshot failed — continuing without rollback point"
74
+ PRE_UPDATE_SNAP=$(ls -t "$TARGET/.cortexhawk-snapshots"/*.json 2>/dev/null | head -1)
75
+ [ -n "$PRE_UPDATE_SNAP" ] && echo " Saved: $(basename "$PRE_UPDATE_SNAP")"
76
+ echo ""
77
+ fi
78
+
79
+ # 4. Pull source (skip in dry-run — compare against current source)
80
+ if [ "$DRY_RUN" != true ]; then
81
+ if [ "$source_type" = "git" ]; then
82
+ echo "Updating CortexHawk source via git pull..."
83
+ update_source_git
84
+ else
85
+ echo "Updating CortexHawk source via download..."
86
+ update_source_release
87
+ fi
88
+ echo " Source updated successfully."
89
+ fi
90
+
91
+ # 5. Compare versions
92
+ local new_version
93
+ new_version=$(get_version)
94
+ echo " New version: $new_version"
95
+ echo ""
96
+
97
+ if [ "$current_version" = "$new_version" ] && [ "$FORCE_MODE" != true ]; then
98
+ # Same version — check if files actually changed (checksum comparison)
99
+ local files_changed=0
100
+ if [ -f "$manifest" ]; then
101
+ while IFS= read -r line; do
102
+ local fpath fhash
103
+ fpath=$(echo "$line" | sed 's/.*"\([^"]*\)": "sha256:\([^"]*\)".*/\1/')
104
+ fhash=$(echo "$line" | sed 's/.*"sha256:\([^"]*\)".*/\1/')
105
+ [ -z "$fpath" ] || [ -z "$fhash" ] && continue
106
+ local source_file="$SCRIPT_DIR/$fpath"
107
+ [ -f "$source_file" ] || continue
108
+ local source_hash
109
+ source_hash=$(compute_checksum "$source_file")
110
+ if [ "$fhash" != "$source_hash" ]; then
111
+ files_changed=$((files_changed + 1))
112
+ fi
113
+ done < <(grep '"sha256:' "$manifest")
114
+ fi
115
+
116
+ if [ "$files_changed" -eq 0 ] && [ "$DRY_RUN" != true ]; then
117
+ # Still apply install improvements even if no component files changed
118
+ local target_dir_name=".${TARGET_CLI:-claude}"
119
+ update_gitignore "$(dirname "$TARGET")" "$target_dir_name"
120
+ [ "$TARGET_CLI" = "codex" ] && update_gitignore "$(dirname "$TARGET")" ".agents"
121
+ if [ ! -f "$TARGET/git-workflow.conf" ]; then
122
+ GIT_BRANCHING="direct-main"
123
+ GIT_COMMIT_CONVENTION="conventional"
124
+ GIT_PR_PREFERENCE="on-demand"
125
+ GIT_AUTO_PUSH="after-commit"
126
+ GIT_WORK_BRANCH=""
127
+ source "$SCRIPT_DIR/scripts/git-workflow-init.sh" "$(dirname "$TARGET")" "$TARGET"
128
+ fi
129
+ echo "Already up to date ($new_version). No component files changed."
130
+ cleanup_update
131
+ exit 0
132
+ elif [ "$files_changed" -gt 0 ]; then
133
+ echo " Same version ($new_version) but $files_changed file(s) changed in source."
134
+ fi
135
+ fi
136
+
137
+ if [ "$DRY_RUN" = true ]; then
138
+ echo " Comparing source $new_version vs installed $current_version"
139
+ elif [ "$current_version" = "$new_version" ]; then
140
+ echo " Syncing changed files ($new_version)..."
141
+ else
142
+ echo " Updating $current_version -> $new_version"
143
+ fi
144
+ echo ""
145
+
146
+ # Set up profile file for skill filtering
147
+ if [ -n "$update_profile" ] && [ "$update_profile" != "all" ]; then
148
+ PROFILE_FILE="$SCRIPT_DIR/profiles/${update_profile}.json"
149
+ if [ ! -f "$PROFILE_FILE" ]; then
150
+ echo " Warning: profile '$update_profile' not found in source — installing all skills"
151
+ update_profile="all"
152
+ PROFILE_FILE=""
153
+ fi
154
+ fi
155
+
156
+ # 6. Reset counters and sync components
157
+ SYNC_ADDED=0
158
+ SYNC_UPDATED=0
159
+ SYNC_UNCHANGED=0
160
+ SYNC_SKIPPED=0
161
+ SYNC_CONFLICTS=0
162
+
163
+ sync_all_components "$update_profile"
164
+
165
+ # 6b. Sync agent personas from project root
166
+ local project_root
167
+ project_root="$(dirname "$TARGET")"
168
+ if [ -d "$project_root/.cortexhawk-agents" ] && [ "$DRY_RUN" != true ]; then
169
+ cp -r "$project_root/.cortexhawk-agents/"*.md "$TARGET/agents/" 2>/dev/null || true
170
+ local pc
171
+ pc=$(find "$project_root/.cortexhawk-agents" -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
172
+ [ "$pc" -gt 0 ] && echo " Synced $pc agent persona(s) from .cortexhawk-agents/"
173
+ fi
174
+
175
+ # 7. Detect removed upstream files
176
+ if [ -f "$manifest" ]; then
177
+ while IFS= read -r line; do
178
+ local file_relpath
179
+ file_relpath=$(echo "$line" | sed 's/.*"\([^"]*\)": "sha256:.*/\1/')
180
+ [ -z "$file_relpath" ] && continue
181
+ if [ ! -f "$SCRIPT_DIR/$file_relpath" ] && [ -f "$TARGET/$file_relpath" ]; then
182
+ echo " Warning: $file_relpath was removed upstream (kept locally)"
183
+ fi
184
+ done < <(grep '"sha256:' "$manifest")
185
+ fi
186
+
187
+ if [ "$DRY_RUN" != true ]; then
188
+ # Make executable components executable
189
+ for entry in "${COMPONENTS[@]}"; do
190
+ IFS=':' read -r name exec_flag <<< "$entry"
191
+ [ "$exec_flag" = "yes" ] && chmod +x "$TARGET/$name/"*.sh 2>/dev/null || true
192
+ done
193
+
194
+ # 7b. Regenerate settings.json hooks + merge new permissions from compose.yml
195
+ if [ -f "$SCRIPT_DIR/hooks/compose.yml" ]; then
196
+ local hooks_json
197
+ hooks_json=$(generate_hooks_config "$SCRIPT_DIR/hooks/compose.yml" ".claude/hooks")
198
+ if [ -n "$hooks_json" ] && [ "$hooks_json" != "{}" ] && command -v python3 >/dev/null 2>&1; then
199
+ echo "$hooks_json" | python3 -c "
200
+ import json, sys
201
+ hooks = json.load(sys.stdin)
202
+ current = {}
203
+ try:
204
+ with open(sys.argv[1]) as f:
205
+ current = json.load(f)
206
+ except:
207
+ pass
208
+ current['hooks'] = hooks
209
+ # Merge new permissions from source
210
+ try:
211
+ with open(sys.argv[2]) as f:
212
+ src_perms = json.load(f).get('permissions', {})
213
+ cur_perms = current.get('permissions', {})
214
+ for key in ('allow', 'deny'):
215
+ src_list = src_perms.get(key, [])
216
+ cur_list = cur_perms.get(key, [])
217
+ added = [p for p in src_list if p not in cur_list]
218
+ if added:
219
+ cur_list.extend(added)
220
+ cur_perms[key] = cur_list
221
+ current['permissions'] = cur_perms
222
+ except:
223
+ pass
224
+ with open(sys.argv[1], 'w') as f:
225
+ json.dump(current, f, indent=2)
226
+ f.write('\n')
227
+ " "$TARGET/settings.json" "$SCRIPT_DIR/settings.json"
228
+ echo " Regenerated settings.json hooks from compose.yml"
229
+ fi
230
+ fi
231
+
232
+ # 8. Write new manifest
233
+ write_manifest "$TARGET" "$update_profile" "$TARGET_CLI" true
234
+
235
+ # 9. Run audit
236
+ run_audit "$(dirname "$TARGET")"
237
+
238
+ # 10. Apply install improvements (gitignore, git-workflow defaults)
239
+ local target_dir_name=".${TARGET_CLI:-claude}"
240
+ update_gitignore "$(dirname "$TARGET")" "$target_dir_name"
241
+ [ "$TARGET_CLI" = "codex" ] && update_gitignore "$(dirname "$TARGET")" ".agents"
242
+ setup_templates "$(dirname "$TARGET")"
243
+
244
+ if [ ! -f "$TARGET/git-workflow.conf" ]; then
245
+ GIT_BRANCHING="direct-main"
246
+ GIT_COMMIT_CONVENTION="conventional"
247
+ GIT_PR_PREFERENCE="on-demand"
248
+ GIT_AUTO_PUSH="after-commit"
249
+ GIT_WORK_BRANCH=""
250
+ source "$SCRIPT_DIR/scripts/git-workflow-init.sh" "$(dirname "$TARGET")" "$TARGET"
251
+ fi
252
+ fi
253
+
254
+ # 10. Print summary
255
+ echo ""
256
+ if [ "$DRY_RUN" = true ]; then
257
+ echo "Dry run summary:"
258
+ echo " Would add: $SYNC_ADDED"
259
+ echo " Would update: $SYNC_UPDATED"
260
+ echo " Unchanged: $SYNC_UNCHANGED"
261
+ echo " Would skip: $SYNC_SKIPPED"
262
+ echo " Conflicts: $SYNC_CONFLICTS"
263
+ echo ""
264
+ echo "No files were modified (dry run)."
265
+ else
266
+ echo "Update complete: $current_version -> $new_version"
267
+ echo " Added: $SYNC_ADDED"
268
+ echo " Updated: $SYNC_UPDATED"
269
+ echo " Unchanged: $SYNC_UNCHANGED"
270
+ echo " Skipped: $SYNC_SKIPPED"
271
+ echo " Conflicts: $SYNC_CONFLICTS"
272
+ if [ -n "${PRE_UPDATE_SNAP:-}" ]; then
273
+ echo " Rollback: install.sh --restore $PRE_UPDATE_SNAP"
274
+ fi
275
+ echo ""
276
+ echo " To activate: exit your CLI (ctrl+c) and relaunch in this directory."
277
+ fi
278
+
279
+ cleanup_update
280
+ }
package/settings.json CHANGED
@@ -14,7 +14,18 @@
14
14
  "Write(*)",
15
15
  "Edit(*)"
16
16
  ],
17
- "deny": []
17
+ "deny": [
18
+ "Write(/etc/*)",
19
+ "Write(~/.bashrc)",
20
+ "Write(~/.zshrc)",
21
+ "Write(~/.profile)",
22
+ "Write(~/.ssh/*)",
23
+ "Edit(/etc/*)",
24
+ "Edit(~/.bashrc)",
25
+ "Edit(~/.zshrc)",
26
+ "Edit(~/.profile)",
27
+ "Edit(~/.ssh/*)"
28
+ ]
18
29
  },
19
30
  "hooks": {
20
31
  "SessionStart": [