@trac3er/oh-my-god 2.0.0 → 2.0.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/.claude-plugin/marketplace.json +8 -8
- package/.claude-plugin/plugin.json +5 -4
- package/.claude-plugin/scripts/uninstall.sh +74 -3
- package/.claude-plugin/scripts/update.sh +78 -3
- package/.coveragerc +26 -0
- package/.mcp.json +4 -4
- package/CHANGELOG.md +14 -0
- package/CODE_OF_CONDUCT.md +27 -0
- package/CONTRIBUTING.md +62 -0
- package/OMG-setup.sh +1201 -355
- package/README.md +77 -56
- package/SECURITY.md +25 -0
- package/agents/__init__.py +1 -0
- package/agents/model_roles.py +196 -0
- package/agents/omg-architect-mode.md +3 -5
- package/agents/omg-backend-engineer.md +3 -5
- package/agents/omg-database-engineer.md +3 -5
- package/agents/omg-frontend-designer.md +4 -5
- package/agents/omg-implement-mode.md +4 -5
- package/agents/omg-infra-engineer.md +3 -5
- package/agents/omg-research-mode.md +4 -6
- package/agents/omg-security-auditor.md +3 -5
- package/agents/omg-testing-engineer.md +3 -5
- package/build/lib/yaml.py +321 -0
- package/commands/OMG:ai-commit.md +101 -14
- package/commands/OMG:arch.md +302 -19
- package/commands/OMG:ccg.md +12 -7
- package/commands/OMG:compat.md +25 -17
- package/commands/OMG:cost.md +173 -13
- package/commands/OMG:crazy.md +1 -1
- package/commands/OMG:create-agent.md +170 -20
- package/commands/OMG:deps.md +235 -17
- package/commands/OMG:domain-init.md +1 -1
- package/commands/OMG:escalate.md +41 -12
- package/commands/OMG:health-check.md +37 -13
- package/commands/OMG:init.md +122 -14
- package/commands/OMG:project-init.md +1 -1
- package/commands/OMG:session-branch.md +76 -9
- package/commands/OMG:session-fork.md +42 -5
- package/commands/OMG:session-merge.md +124 -8
- package/commands/OMG:setup.md +69 -12
- package/commands/OMG:stats.md +215 -14
- package/commands/OMG:teams.md +19 -10
- package/config/lsp_languages.yaml +8 -0
- package/hooks/__init__.py +0 -0
- package/hooks/_agent_registry.py +423 -0
- package/hooks/_analytics.py +291 -0
- package/hooks/_budget.py +31 -0
- package/hooks/_common.py +569 -0
- package/hooks/_compression_optimizer.py +119 -0
- package/hooks/_cost_ledger.py +176 -0
- package/hooks/_learnings.py +126 -0
- package/hooks/_memory.py +103 -0
- package/hooks/_protected_context.py +150 -0
- package/hooks/_token_counter.py +221 -0
- package/hooks/branch_manager.py +236 -0
- package/hooks/budget_governor.py +232 -0
- package/hooks/circuit-breaker.py +270 -0
- package/hooks/compression_feedback.py +254 -0
- package/hooks/config-guard.py +216 -0
- package/hooks/context_pressure.py +53 -0
- package/hooks/credential_store.py +1020 -0
- package/hooks/fetch-rate-limits.py +212 -0
- package/hooks/firewall.py +48 -0
- package/hooks/hashline-formatter-bridge.py +224 -0
- package/hooks/hashline-injector.py +273 -0
- package/hooks/hashline-validator.py +216 -0
- package/hooks/idle-detector.py +95 -0
- package/hooks/intentgate-keyword-detector.py +188 -0
- package/hooks/magic-keyword-router.py +195 -0
- package/hooks/policy_engine.py +505 -0
- package/hooks/post-tool-failure.py +19 -0
- package/hooks/post-write.py +219 -0
- package/hooks/post_write.py +46 -0
- package/hooks/pre-compact.py +398 -0
- package/hooks/pre-tool-inject.py +98 -0
- package/hooks/prompt-enhancer.py +672 -0
- package/hooks/quality-runner.py +191 -0
- package/hooks/query.py +512 -0
- package/hooks/secret-guard.py +61 -0
- package/hooks/secret_audit.py +144 -0
- package/hooks/session-end-capture.py +137 -0
- package/hooks/session-start.py +277 -0
- package/hooks/setup_wizard.py +582 -0
- package/hooks/shadow_manager.py +297 -0
- package/hooks/state_migration.py +225 -0
- package/hooks/stop-gate.py +7 -0
- package/hooks/stop_dispatcher.py +945 -0
- package/hooks/test-validator.py +361 -0
- package/hooks/test_generator_hook.py +123 -0
- package/hooks/todo-state-tracker.py +114 -0
- package/hooks/tool-ledger.py +149 -0
- package/hooks/trust_review.py +585 -0
- package/hud/omg-hud.mjs +31 -1
- package/lab/__init__.py +1 -0
- package/lab/pipeline.py +75 -0
- package/lab/policies.py +52 -0
- package/package.json +7 -18
- package/plugins/README.md +33 -61
- package/plugins/advanced/commands/OMG:deep-plan.md +3 -3
- package/plugins/advanced/commands/OMG:learn.md +1 -1
- package/plugins/advanced/commands/OMG:security-review.md +3 -3
- package/plugins/advanced/commands/OMG:ship.md +1 -1
- package/plugins/advanced/plugin.json +1 -1
- package/plugins/core/plugin.json +8 -3
- package/plugins/dephealth/__init__.py +0 -0
- package/plugins/dephealth/cve_scanner.py +188 -0
- package/plugins/dephealth/license_checker.py +135 -0
- package/plugins/dephealth/manifest_detector.py +423 -0
- package/plugins/dephealth/vuln_analyzer.py +169 -0
- package/plugins/testgen/__init__.py +0 -0
- package/plugins/testgen/codamosa_engine.py +402 -0
- package/plugins/testgen/edge_case_synthesizer.py +184 -0
- package/plugins/testgen/framework_detector.py +271 -0
- package/plugins/testgen/skeleton_generator.py +219 -0
- package/plugins/viz/__init__.py +0 -0
- package/plugins/viz/ast_parser.py +139 -0
- package/plugins/viz/diagram_generator.py +192 -0
- package/plugins/viz/graph_builder.py +444 -0
- package/plugins/viz/native_parsers.py +259 -0
- package/plugins/viz/regex_parser.py +112 -0
- package/pyproject.toml +81 -0
- package/rules/contextual/write-verify.md +2 -2
- package/rules/core/00-truth.md +1 -1
- package/rules/core/01-surgical.md +1 -1
- package/rules/core/02-circuit-breaker.md +2 -2
- package/rules/core/03-ensemble.md +3 -3
- package/rules/core/04-testing.md +3 -3
- package/runtime/__init__.py +32 -0
- package/runtime/adapters/__init__.py +13 -0
- package/runtime/adapters/claude.py +60 -0
- package/runtime/adapters/gpt.py +53 -0
- package/runtime/adapters/local.py +53 -0
- package/runtime/adoption.py +212 -0
- package/runtime/business_workflow.py +220 -0
- package/runtime/cli_provider.py +85 -0
- package/runtime/compat.py +1299 -0
- package/runtime/custom_agent_loader.py +366 -0
- package/runtime/dispatcher.py +47 -0
- package/runtime/ecosystem.py +371 -0
- package/runtime/legacy_compat.py +7 -0
- package/runtime/mcp_config_writers.py +115 -0
- package/runtime/mcp_lifecycle.py +153 -0
- package/runtime/mcp_memory_server.py +135 -0
- package/runtime/memory_parsers/__init__.py +0 -0
- package/runtime/memory_parsers/chatgpt_parser.py +257 -0
- package/runtime/memory_parsers/claude_import.py +107 -0
- package/runtime/memory_parsers/export.py +97 -0
- package/runtime/memory_parsers/gemini_import.py +91 -0
- package/runtime/memory_parsers/kimi_import.py +91 -0
- package/runtime/memory_store.py +215 -0
- package/runtime/omc_compat.py +7 -0
- package/runtime/providers/__init__.py +0 -0
- package/runtime/providers/codex_provider.py +112 -0
- package/runtime/providers/gemini_provider.py +128 -0
- package/runtime/providers/kimi_provider.py +151 -0
- package/runtime/providers/opencode_provider.py +144 -0
- package/runtime/subagent_dispatcher.py +362 -0
- package/runtime/team_router.py +1167 -0
- package/runtime/tmux_session_manager.py +169 -0
- package/scripts/check-omg-compat-contract-snapshot.py +137 -0
- package/scripts/check-omg-contract-snapshot.py +12 -0
- package/scripts/check-omg-public-ready.py +193 -0
- package/scripts/check-omg-standalone-clean.py +103 -0
- package/scripts/legacy_to_omg_migrate.py +29 -0
- package/scripts/migrate-legacy.py +464 -0
- package/scripts/omc_to_omg_migrate.py +12 -0
- package/scripts/omg.py +492 -0
- package/scripts/settings-merge.py +283 -0
- package/scripts/verify-standalone.sh +8 -4
- package/settings.json +126 -29
- package/templates/profile.yaml +1 -1
- package/tools/__init__.py +2 -0
- package/tools/browser_consent.py +289 -0
- package/tools/browser_stealth.py +481 -0
- package/tools/browser_tool.py +448 -0
- package/tools/changelog_generator.py +347 -0
- package/tools/commit_splitter.py +746 -0
- package/tools/config_discovery.py +151 -0
- package/tools/config_merger.py +449 -0
- package/tools/dashboard_generator.py +300 -0
- package/tools/git_inspector.py +298 -0
- package/tools/lsp_client.py +275 -0
- package/tools/lsp_discovery.py +231 -0
- package/tools/lsp_operations.py +392 -0
- package/tools/pr_generator.py +404 -0
- package/tools/python_repl.py +656 -0
- package/tools/python_sandbox.py +609 -0
- package/tools/search_providers/__init__.py +77 -0
- package/tools/search_providers/brave.py +115 -0
- package/tools/search_providers/exa.py +116 -0
- package/tools/search_providers/jina.py +104 -0
- package/tools/search_providers/perplexity.py +139 -0
- package/tools/search_providers/synthetic.py +74 -0
- package/tools/session_snapshot.py +736 -0
- package/tools/ssh_manager.py +912 -0
- package/tools/theme_engine.py +294 -0
- package/tools/theme_selector.py +137 -0
- package/tools/web_search.py +622 -0
- package/yaml.py +321 -0
- package/.claude-plugin/scripts/install.sh +0 -9
- package/bun.lock +0 -23
- package/bunfig.toml +0 -3
- package/hooks/_budget.ts +0 -1
- package/hooks/_common.ts +0 -63
- package/hooks/circuit-breaker.ts +0 -101
- package/hooks/config-guard.ts +0 -4
- package/hooks/firewall.ts +0 -20
- package/hooks/policy_engine.ts +0 -156
- package/hooks/post-tool-failure.ts +0 -22
- package/hooks/post-write.ts +0 -4
- package/hooks/pre-tool-inject.ts +0 -4
- package/hooks/prompt-enhancer.ts +0 -46
- package/hooks/quality-runner.ts +0 -24
- package/hooks/secret-guard.ts +0 -4
- package/hooks/session-end-capture.ts +0 -19
- package/hooks/session-start.ts +0 -19
- package/hooks/shadow_manager.ts +0 -81
- package/hooks/stop-gate.ts +0 -22
- package/hooks/stop_dispatcher.ts +0 -147
- package/hooks/test-generator-hook.ts +0 -4
- package/hooks/tool-ledger.ts +0 -27
- package/hooks/trust_review.ts +0 -175
- package/lab/pipeline.ts +0 -75
- package/lab/policies.ts +0 -68
- package/runtime/common.ts +0 -111
- package/runtime/compat.ts +0 -174
- package/runtime/dispatcher.ts +0 -25
- package/runtime/ecosystem.ts +0 -186
- package/runtime/provider_bootstrap.ts +0 -99
- package/runtime/provider_smoke.ts +0 -34
- package/runtime/release_readiness.ts +0 -186
- package/runtime/team_router.ts +0 -144
- package/scripts/check-omg-compat-contract-snapshot.ts +0 -20
- package/scripts/check-omg-standalone-clean.ts +0 -12
- package/scripts/check-runtime-clean.ts +0 -94
- package/scripts/omg.ts +0 -352
- package/scripts/settings-merge.ts +0 -93
- package/tools/commit_splitter.ts +0 -23
- package/tools/git_inspector.ts +0 -18
- package/tools/session_snapshot.ts +0 -47
- package/trac3er-oh-my-god-2.0.0.tgz +0 -0
- package/tsconfig.json +0 -15
package/OMG-setup.sh
CHANGED
|
@@ -3,6 +3,19 @@ set -euo pipefail
|
|
|
3
3
|
|
|
4
4
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
5
5
|
CLAUDE_DIR="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
|
|
6
|
+
BACKUP_TS="$(date +%Y%m%d_%H%M%S)"
|
|
7
|
+
BACKUP_DIR="$CLAUDE_DIR/.omg-backup-$BACKUP_TS"
|
|
8
|
+
VERSION="2.0.2"
|
|
9
|
+
|
|
10
|
+
PLUGIN_NAME="omg"
|
|
11
|
+
PLUGIN_MARKETPLACE="omg"
|
|
12
|
+
LEGACY_PLUGIN_MARKETPLACE="oh-advanced-layer"
|
|
13
|
+
PLUGIN_REF="${PLUGIN_NAME}@${PLUGIN_MARKETPLACE}"
|
|
14
|
+
LEGACY_PLUGIN_REF="${PLUGIN_NAME}@${LEGACY_PLUGIN_MARKETPLACE}"
|
|
15
|
+
PLUGIN_CACHE_DIR="$CLAUDE_DIR/plugins/cache/$PLUGIN_MARKETPLACE/$PLUGIN_NAME"
|
|
16
|
+
LEGACY_PLUGIN_CACHE_DIR="$CLAUDE_DIR/plugins/cache/$LEGACY_PLUGIN_MARKETPLACE/$PLUGIN_NAME"
|
|
17
|
+
PLUGIN_BUNDLE_MARKER_FILE=".omg-plugin-bundle"
|
|
18
|
+
|
|
6
19
|
ACTION="install"
|
|
7
20
|
ACTION_EXPLICIT=false
|
|
8
21
|
DRY_RUN=false
|
|
@@ -11,421 +24,1254 @@ MERGE_POLICY="ask"
|
|
|
11
24
|
FRESH_INSTALL=false
|
|
12
25
|
INSTALL_AS_PLUGIN=false
|
|
13
26
|
USE_SYMLINK=false
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
27
|
+
ADOPTION_MODE="omg-only"
|
|
28
|
+
ADOPT_MODE="auto"
|
|
29
|
+
OMG_PRESET="safe"
|
|
30
|
+
ERRORS=0
|
|
31
|
+
OMG_MANIFEST="$CLAUDE_DIR/.omg-manifest"
|
|
32
|
+
NEW_MANIFEST_ENTRIES=()
|
|
33
|
+
|
|
34
|
+
V3_RULES=(
|
|
35
|
+
"00-truth-evidence.md" "01-enforcement-map.md" "02-doc-check.md"
|
|
36
|
+
"03-working-memory.md" "04-quality-gate.md" "05-structured-reports.md"
|
|
37
|
+
"06-infra-safety.md" "07-cross-model.md" "08-big-picture.md"
|
|
38
|
+
"09-surgical-changes.md" "10-code-simplifier.md" "11-dependency-safety.md"
|
|
39
|
+
"12-circuit-breaker.md" "13-planning-checklist.md" "14-auto-commands.md"
|
|
40
|
+
"15-context-management.md" "16-honest-testing.md" "17-ensemble-collaboration.md"
|
|
41
|
+
"18-collaborative-solving.md" "19-outside-in.md" "20-project-identity.md"
|
|
42
|
+
"21-verified-claims.md" "22-auto-plugin-mcp.md"
|
|
43
|
+
)
|
|
44
|
+
V3_AGENTS_REMOVE=(cross-validator.md dependency-guardian.md infra-guardian.md perf-analyst.md ui-reviewer.md)
|
|
45
|
+
OLD_OMG_AGENTS=(architect.md critic.md executor.md qa-tester.md escalation-router.md)
|
|
46
|
+
V3_COMMANDS_REMOVE=(cross-review.md simplify.md)
|
|
47
|
+
V4_COMMANDS_REMOVE=(
|
|
48
|
+
code-review.md deep-plan.md domain-init.md escalate.md handoff.md
|
|
49
|
+
health-check.md learn.md project-init.md security-review.md
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
# Dynamic hook discovery — no hardcoded list.
|
|
53
|
+
# Used by remove_omg_files() as fallback when manifest is absent.
|
|
54
|
+
build_omg_hooks_list() {
|
|
55
|
+
OMG_HOOKS=()
|
|
56
|
+
for f in "$SCRIPT_DIR"/hooks/*.py; do
|
|
57
|
+
[ -f "$f" ] && OMG_HOOKS+=("$(basename "$f")")
|
|
58
|
+
done
|
|
59
|
+
}
|
|
20
60
|
|
|
21
61
|
usage() {
|
|
22
|
-
|
|
62
|
+
cat <<EOF
|
|
23
63
|
OMG Setup Manager
|
|
24
64
|
|
|
25
65
|
Usage:
|
|
26
|
-
./OMG-setup.sh <action> [
|
|
27
|
-
./OMG-setup.sh [
|
|
66
|
+
./OMG-setup.sh <action> [OPTIONS]
|
|
67
|
+
./OMG-setup.sh [OPTIONS] # defaults to install
|
|
68
|
+
./OMG-setup.sh # interactive menu in terminal mode
|
|
28
69
|
|
|
29
70
|
Actions:
|
|
30
|
-
install Install or
|
|
31
|
-
update Alias of install
|
|
32
|
-
reinstall
|
|
33
|
-
uninstall Remove OMG-managed files
|
|
71
|
+
install Install or upgrade OMG components
|
|
72
|
+
update Alias of install (explicit update mode)
|
|
73
|
+
reinstall Clean reinstall (remove OMG files, then install)
|
|
74
|
+
uninstall Remove OMG-managed files from ~/.claude
|
|
34
75
|
|
|
35
76
|
Options:
|
|
36
|
-
--fresh
|
|
37
|
-
--symlink
|
|
77
|
+
--fresh For install/update: clean reinstall before install
|
|
78
|
+
--symlink Use symlinks instead of copies (dev mode - live updates)
|
|
38
79
|
--install-as-plugin
|
|
39
|
-
|
|
40
|
-
--dry-run
|
|
41
|
-
--non-interactive
|
|
42
|
-
--merge-policy=ask
|
|
43
|
-
-
|
|
80
|
+
Install plugin bundle (plugin.json + MCP + HUD) together
|
|
81
|
+
--dry-run Show what would happen without writing files
|
|
82
|
+
--non-interactive Skip prompts (CI/automation mode)
|
|
83
|
+
--merge-policy=X Settings merge: ask (default), apply, skip
|
|
84
|
+
--mode=omg-only|coexist
|
|
85
|
+
Native OMG adoption mode for overlapping ecosystems
|
|
86
|
+
--adopt=auto Detect OMG-adjacent ecosystems during install/update
|
|
87
|
+
--preset=safe|balanced|interop|labs
|
|
88
|
+
User-facing preset for managed OMG features
|
|
89
|
+
-h, --help Show this help
|
|
90
|
+
|
|
91
|
+
Examples:
|
|
92
|
+
./OMG-setup.sh install
|
|
93
|
+
./OMG-setup.sh install --symlink # Dev mode: live updates from repo
|
|
94
|
+
./OMG-setup.sh install --install-as-plugin
|
|
95
|
+
./OMG-setup.sh install --mode=coexist --preset=interop
|
|
96
|
+
./OMG-setup.sh update --non-interactive --merge-policy=apply
|
|
97
|
+
./OMG-setup.sh reinstall --dry-run
|
|
98
|
+
./OMG-setup.sh uninstall --dry-run
|
|
44
99
|
EOF
|
|
45
100
|
}
|
|
46
101
|
|
|
47
|
-
|
|
48
|
-
|
|
102
|
+
is_standalone_installed() {
|
|
103
|
+
[ -f "$CLAUDE_DIR/hooks/.omg-version" ] || [ -d "$CLAUDE_DIR/omg-runtime" ]
|
|
49
104
|
}
|
|
50
105
|
|
|
51
|
-
|
|
52
|
-
|
|
106
|
+
is_plugin_installed() {
|
|
107
|
+
local marker_new="$PLUGIN_CACHE_DIR/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
108
|
+
local marker_legacy="$LEGACY_PLUGIN_CACHE_DIR/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
109
|
+
if [ -f "$marker_new" ] || [ -f "$marker_legacy" ]; then
|
|
110
|
+
return 0
|
|
111
|
+
fi
|
|
112
|
+
local installed_plugins="$CLAUDE_DIR/plugins/installed_plugins.json"
|
|
113
|
+
if [ -f "$installed_plugins" ] && grep -Eq "\"$PLUGIN_REF\"|\"$LEGACY_PLUGIN_REF\"" "$installed_plugins" 2>/dev/null; then
|
|
114
|
+
return 0
|
|
115
|
+
fi
|
|
116
|
+
return 1
|
|
53
117
|
}
|
|
54
118
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
fi
|
|
60
|
-
"$@"
|
|
61
|
-
}
|
|
119
|
+
prompt_start_action() {
|
|
120
|
+
if $ACTION_EXPLICIT || $NON_INTERACTIVE || $DRY_RUN; then
|
|
121
|
+
return 0
|
|
122
|
+
fi
|
|
62
123
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
124
|
+
local standalone_installed=false
|
|
125
|
+
local plugin_installed=false
|
|
126
|
+
is_standalone_installed && standalone_installed=true
|
|
127
|
+
is_plugin_installed && plugin_installed=true
|
|
128
|
+
|
|
129
|
+
echo ""
|
|
130
|
+
echo "Select OMG setup action:"
|
|
131
|
+
echo " 1. Install standalone"
|
|
132
|
+
if $standalone_installed; then
|
|
133
|
+
echo " 2. Update standalone"
|
|
134
|
+
fi
|
|
135
|
+
echo " 3. Install as plugin"
|
|
136
|
+
if $plugin_installed; then
|
|
137
|
+
echo " 4. Update plugin install"
|
|
138
|
+
fi
|
|
139
|
+
echo " 5. Uninstall"
|
|
140
|
+
echo " 0. Cancel"
|
|
141
|
+
echo ""
|
|
81
142
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
143
|
+
read -p "Choose [1/2/3/4/5/0]: " -r
|
|
144
|
+
case "${REPLY:-}" in
|
|
145
|
+
1)
|
|
146
|
+
ACTION="install"
|
|
147
|
+
INSTALL_AS_PLUGIN=false
|
|
148
|
+
;;
|
|
149
|
+
2)
|
|
150
|
+
if $standalone_installed; then
|
|
151
|
+
ACTION="update"
|
|
152
|
+
INSTALL_AS_PLUGIN=false
|
|
153
|
+
else
|
|
154
|
+
echo "Standalone update unavailable (not installed)."
|
|
155
|
+
exit 1
|
|
156
|
+
fi
|
|
157
|
+
;;
|
|
158
|
+
3)
|
|
159
|
+
ACTION="install"
|
|
160
|
+
INSTALL_AS_PLUGIN=true
|
|
161
|
+
;;
|
|
162
|
+
4)
|
|
163
|
+
if $plugin_installed; then
|
|
164
|
+
ACTION="update"
|
|
165
|
+
INSTALL_AS_PLUGIN=true
|
|
166
|
+
else
|
|
167
|
+
echo "Plugin update unavailable (plugin install not detected)."
|
|
168
|
+
exit 1
|
|
169
|
+
fi
|
|
170
|
+
;;
|
|
171
|
+
5)
|
|
172
|
+
ACTION="uninstall"
|
|
173
|
+
;;
|
|
174
|
+
0)
|
|
175
|
+
echo "Cancelled by user."
|
|
176
|
+
exit 0
|
|
177
|
+
;;
|
|
178
|
+
*)
|
|
179
|
+
echo "Invalid selection."
|
|
180
|
+
exit 1
|
|
181
|
+
;;
|
|
182
|
+
esac
|
|
93
183
|
}
|
|
94
184
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
185
|
+
parse_args() {
|
|
186
|
+
if [ $# -gt 0 ]; then
|
|
187
|
+
case "$1" in
|
|
188
|
+
install|update|reinstall|uninstall)
|
|
189
|
+
ACTION="$1"
|
|
190
|
+
ACTION_EXPLICIT=true
|
|
191
|
+
shift
|
|
192
|
+
;;
|
|
193
|
+
help|-h|--help)
|
|
194
|
+
usage
|
|
195
|
+
exit 0
|
|
196
|
+
;;
|
|
197
|
+
esac
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
for arg in "$@"; do
|
|
201
|
+
case "$arg" in
|
|
202
|
+
--dry-run) DRY_RUN=true ;;
|
|
203
|
+
--symlink) USE_SYMLINK=true ;;
|
|
204
|
+
--non-interactive) NON_INTERACTIVE=true ;;
|
|
205
|
+
--fresh) FRESH_INSTALL=true ;;
|
|
206
|
+
--install-as-plugin) INSTALL_AS_PLUGIN=true ;;
|
|
207
|
+
--merge-policy=*) MERGE_POLICY="${arg#*=}" ;;
|
|
208
|
+
--mode=*) ADOPTION_MODE="${arg#*=}" ;;
|
|
209
|
+
--adopt=*) ADOPT_MODE="${arg#*=}" ;;
|
|
210
|
+
--preset=*) OMG_PRESET="${arg#*=}" ;;
|
|
211
|
+
--help|-h)
|
|
212
|
+
usage
|
|
213
|
+
exit 0
|
|
214
|
+
;;
|
|
215
|
+
*)
|
|
216
|
+
echo "Unknown option: $arg"
|
|
217
|
+
echo ""
|
|
218
|
+
usage
|
|
219
|
+
exit 1
|
|
220
|
+
;;
|
|
221
|
+
esac
|
|
222
|
+
done
|
|
223
|
+
|
|
224
|
+
if [ "$ACTION" = "reinstall" ]; then
|
|
225
|
+
FRESH_INSTALL=true
|
|
226
|
+
fi
|
|
227
|
+
|
|
228
|
+
if [ ! -t 0 ] || [ -n "${npm_lifecycle_event:-}" ] || [ -n "${npm_execpath:-}" ]; then
|
|
229
|
+
NON_INTERACTIVE=true
|
|
230
|
+
fi
|
|
231
|
+
|
|
232
|
+
# Auto-enable plugin mode for npm installs
|
|
233
|
+
if [ -n "${npm_execpath:-}" ] || [ -n "${npm_lifecycle_event:-}" ]; then
|
|
234
|
+
INSTALL_AS_PLUGIN=true
|
|
235
|
+
fi
|
|
236
|
+
|
|
237
|
+
case "$ADOPTION_MODE" in
|
|
238
|
+
omg-only|coexist) ;;
|
|
239
|
+
*)
|
|
240
|
+
echo "Unknown adoption mode: $ADOPTION_MODE"
|
|
241
|
+
exit 1
|
|
242
|
+
;;
|
|
243
|
+
esac
|
|
244
|
+
|
|
245
|
+
case "$ADOPT_MODE" in
|
|
246
|
+
auto) ;;
|
|
247
|
+
*)
|
|
248
|
+
echo "Unknown adoption detector mode: $ADOPT_MODE"
|
|
249
|
+
exit 1
|
|
250
|
+
;;
|
|
114
251
|
esac
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
252
|
+
|
|
253
|
+
case "$OMG_PRESET" in
|
|
254
|
+
safe|balanced|interop|labs) ;;
|
|
255
|
+
*)
|
|
256
|
+
echo "Unknown OMG preset: $OMG_PRESET"
|
|
257
|
+
exit 1
|
|
258
|
+
;;
|
|
119
259
|
esac
|
|
120
|
-
copy_file "$src" "$dst_dir/$rel" "$mode"
|
|
121
|
-
done < <(find "$src_dir" -type f | sort)
|
|
122
260
|
}
|
|
123
261
|
|
|
124
|
-
|
|
125
|
-
|
|
262
|
+
preflight() {
|
|
263
|
+
echo "Pre-flight checks..."
|
|
264
|
+
if ! command -v python3 &>/dev/null; then
|
|
265
|
+
echo " ❌ python3 not found. Install: https://www.python.org/downloads/"
|
|
266
|
+
exit 1
|
|
267
|
+
fi
|
|
268
|
+
local py_ver py_maj py_min
|
|
269
|
+
py_ver=$(python3 -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')")
|
|
270
|
+
py_maj=$(echo "$py_ver" | cut -d. -f1)
|
|
271
|
+
py_min=$(echo "$py_ver" | cut -d. -f2)
|
|
272
|
+
if [ "$py_maj" -lt 3 ] || { [ "$py_maj" -eq 3 ] && [ "$py_min" -lt 8 ]; }; then
|
|
273
|
+
echo " ❌ Python $py_ver found, 3.8+ required"
|
|
274
|
+
exit 1
|
|
275
|
+
fi
|
|
276
|
+
echo " ✓ Python $py_ver"
|
|
126
277
|
}
|
|
127
278
|
|
|
128
|
-
|
|
129
|
-
|
|
279
|
+
ensure_backup() {
|
|
280
|
+
if ! $DRY_RUN; then
|
|
281
|
+
mkdir -p "$BACKUP_DIR"
|
|
282
|
+
for dir in rules hooks agents commands; do
|
|
283
|
+
[ -d "$CLAUDE_DIR/$dir" ] && cp -r "$CLAUDE_DIR/$dir" "$BACKUP_DIR/$dir" 2>/dev/null || true
|
|
284
|
+
done
|
|
285
|
+
[ -f "$CLAUDE_DIR/settings.json" ] && cp "$CLAUDE_DIR/settings.json" "$BACKUP_DIR/"
|
|
286
|
+
prune_old_backups
|
|
287
|
+
fi
|
|
130
288
|
}
|
|
131
289
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
fi
|
|
152
|
-
say " 5. Uninstall"
|
|
153
|
-
say " 0. Cancel"
|
|
154
|
-
say ""
|
|
155
|
-
read -r -p "Choose [1/2/3/4/5/0]: " choice
|
|
156
|
-
case "${choice:-}" in
|
|
157
|
-
1) ACTION="install"; INSTALL_AS_PLUGIN=false ;;
|
|
158
|
-
2) ACTION="update"; INSTALL_AS_PLUGIN=false ;;
|
|
159
|
-
3) ACTION="install"; INSTALL_AS_PLUGIN=true ;;
|
|
160
|
-
4) ACTION="update"; INSTALL_AS_PLUGIN=true ;;
|
|
161
|
-
5) ACTION="uninstall" ;;
|
|
162
|
-
0) say "Cancelled by user."; exit 0 ;;
|
|
163
|
-
*) say "Invalid selection."; exit 1 ;;
|
|
164
|
-
esac
|
|
290
|
+
prune_old_backups() {
|
|
291
|
+
local backups=()
|
|
292
|
+
while IFS= read -r path; do
|
|
293
|
+
backups+=("$path")
|
|
294
|
+
done < <(find "$CLAUDE_DIR" -maxdepth 1 -type d -name ".omg-backup-*" | sort)
|
|
295
|
+
|
|
296
|
+
local total=${#backups[@]}
|
|
297
|
+
if [ "$total" -le 2 ]; then
|
|
298
|
+
return 0
|
|
299
|
+
fi
|
|
300
|
+
|
|
301
|
+
local remove_count=$((total - 2))
|
|
302
|
+
for old in "${backups[@]:0:$remove_count}"; do
|
|
303
|
+
[[ "$old" == "$CLAUDE_DIR"/* ]] || {
|
|
304
|
+
echo "ERROR: backup prune target outside expected directory: $old" >&2
|
|
305
|
+
exit 1
|
|
306
|
+
}
|
|
307
|
+
rm -rf "$old"
|
|
308
|
+
done
|
|
165
309
|
}
|
|
166
310
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
ACTION_EXPLICIT=true
|
|
173
|
-
shift
|
|
174
|
-
;;
|
|
175
|
-
-h|--help|help)
|
|
176
|
-
usage
|
|
177
|
-
exit 0
|
|
178
|
-
;;
|
|
179
|
-
esac
|
|
180
|
-
fi
|
|
181
|
-
|
|
182
|
-
for arg in "$@"; do
|
|
183
|
-
case "$arg" in
|
|
184
|
-
--dry-run) DRY_RUN=true ;;
|
|
185
|
-
--symlink) USE_SYMLINK=true ;;
|
|
186
|
-
--non-interactive) NON_INTERACTIVE=true ;;
|
|
187
|
-
--fresh) FRESH_INSTALL=true ;;
|
|
188
|
-
--install-as-plugin) INSTALL_AS_PLUGIN=true ;;
|
|
189
|
-
--skip-codex-skills) SKIP_CODEX_SKILLS=true ;;
|
|
190
|
-
--merge-policy=*) MERGE_POLICY="${arg#*=}" ;;
|
|
191
|
-
-h|--help)
|
|
192
|
-
usage
|
|
193
|
-
exit 0
|
|
194
|
-
;;
|
|
195
|
-
*)
|
|
196
|
-
say "Unknown option: $arg"
|
|
197
|
-
usage
|
|
198
|
-
exit 1
|
|
199
|
-
;;
|
|
200
|
-
esac
|
|
201
|
-
done
|
|
311
|
+
is_omg_managed_command_file() {
|
|
312
|
+
local file="$1"
|
|
313
|
+
if [ ! -f "$file" ]; then
|
|
314
|
+
return 1
|
|
315
|
+
fi
|
|
202
316
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
317
|
+
if grep -q "OMG-AUTO-COMPAT-ALIAS" "$file" 2>/dev/null; then
|
|
318
|
+
return 0
|
|
319
|
+
fi
|
|
320
|
+
if grep -q "OMG-MANAGED-COMMAND" "$file" 2>/dev/null; then
|
|
321
|
+
return 0
|
|
322
|
+
fi
|
|
207
323
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
324
|
+
local base
|
|
325
|
+
base="$(basename "$file")"
|
|
326
|
+
if [[ "$base" == OMG:* ]] && grep -q "/OMG:" "$file" 2>/dev/null; then
|
|
327
|
+
return 0
|
|
328
|
+
fi
|
|
329
|
+
return 1
|
|
330
|
+
}
|
|
211
331
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
332
|
+
mark_omg_managed_command_file() {
|
|
333
|
+
local file="$1"
|
|
334
|
+
if [ ! -f "$file" ]; then
|
|
335
|
+
return 0
|
|
336
|
+
fi
|
|
337
|
+
if ! grep -q "OMG-MANAGED-COMMAND" "$file" 2>/dev/null; then
|
|
338
|
+
printf "\n<!-- OMG-MANAGED-COMMAND -->\n" >> "$file"
|
|
339
|
+
fi
|
|
215
340
|
}
|
|
216
341
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
342
|
+
# Install a file or directory - either copy or symlink based on USE_SYMLINK
|
|
343
|
+
# Usage: install_file <source> <target> [type: file|dir]
|
|
344
|
+
install_file() {
|
|
345
|
+
local src="$1"
|
|
346
|
+
local target="$2"
|
|
347
|
+
local type="${3:-file}"
|
|
348
|
+
|
|
349
|
+
if $USE_SYMLINK; then
|
|
350
|
+
# In symlink mode, create symlink from target -> source
|
|
351
|
+
# First remove existing file/dir if present
|
|
352
|
+
if [ -e "$target" ] || [ -L "$target" ]; then
|
|
353
|
+
rm -rf "$target"
|
|
354
|
+
fi
|
|
355
|
+
ln -s "$src" "$target"
|
|
356
|
+
else
|
|
357
|
+
# In copy mode, do regular copy
|
|
358
|
+
if [ "$type" = "dir" ]; then
|
|
359
|
+
cp -R "$src" "$target"
|
|
360
|
+
else
|
|
361
|
+
cp "$src" "$target"
|
|
362
|
+
fi
|
|
363
|
+
fi
|
|
229
364
|
}
|
|
230
365
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
if $DRY_RUN; then
|
|
234
|
-
say " DRY RUN: backup -> $backup_dir"
|
|
235
|
-
return 0
|
|
236
|
-
fi
|
|
237
|
-
mkdir -p "$backup_dir"
|
|
238
|
-
for path in settings.json commands agents hooks omg-runtime rules templates; do
|
|
239
|
-
if [ -e "$CLAUDE_DIR/$path" ]; then
|
|
240
|
-
cp -R "$CLAUDE_DIR/$path" "$backup_dir/" 2>/dev/null || true
|
|
241
|
-
fi
|
|
242
|
-
done
|
|
243
|
-
say " ✓ Backup: $backup_dir"
|
|
366
|
+
track_file() {
|
|
367
|
+
NEW_MANIFEST_ENTRIES+=("$1")
|
|
244
368
|
}
|
|
245
369
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
370
|
+
reconcile_stale_files() {
|
|
371
|
+
if [ ! -f "$OMG_MANIFEST" ]; then
|
|
372
|
+
echo " (no previous manifest — first install, skipping reconciliation)"
|
|
373
|
+
return 0
|
|
374
|
+
fi
|
|
375
|
+
local stale=0
|
|
376
|
+
while IFS= read -r old_entry; do
|
|
377
|
+
[ -n "$old_entry" ] || continue
|
|
378
|
+
[[ "$old_entry" == "#"* ]] && continue
|
|
379
|
+
local found=false
|
|
380
|
+
for new_entry in "${NEW_MANIFEST_ENTRIES[@]}"; do
|
|
381
|
+
if [ "$old_entry" = "$new_entry" ]; then
|
|
382
|
+
found=true
|
|
383
|
+
break
|
|
384
|
+
fi
|
|
385
|
+
done
|
|
386
|
+
if ! $found; then
|
|
387
|
+
local target="$CLAUDE_DIR/$old_entry"
|
|
388
|
+
if [ -f "$target" ]; then
|
|
389
|
+
if ! $DRY_RUN; then
|
|
390
|
+
rm -f "$target"
|
|
391
|
+
fi
|
|
392
|
+
echo " - $old_entry (removed from source)"
|
|
393
|
+
stale=$((stale + 1))
|
|
394
|
+
fi
|
|
395
|
+
fi
|
|
396
|
+
done < "$OMG_MANIFEST"
|
|
397
|
+
if [ $stale -eq 0 ]; then
|
|
398
|
+
echo " (no stale files)"
|
|
399
|
+
elif $DRY_RUN; then
|
|
400
|
+
echo " (dry-run: would remove $stale stale file(s))"
|
|
254
401
|
else
|
|
255
|
-
|
|
402
|
+
echo " ✓ Cleaned $stale stale file(s)"
|
|
256
403
|
fi
|
|
257
|
-
done < "$MANIFEST_PATH"
|
|
258
|
-
if ! $DRY_RUN; then
|
|
259
|
-
rm -f "$MANIFEST_PATH"
|
|
260
|
-
fi
|
|
261
404
|
}
|
|
262
405
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
"$CLAUDE_DIR/omg-runtime/scripts"
|
|
268
|
-
"$CLAUDE_DIR/omg-runtime/runtime"
|
|
269
|
-
"$CLAUDE_DIR/omg-runtime/tools"
|
|
270
|
-
"$CLAUDE_DIR/omg-runtime/control_plane"
|
|
271
|
-
"$CLAUDE_DIR/omg-runtime/lab"
|
|
272
|
-
"$CLAUDE_DIR/omg-runtime/omg_natives"
|
|
273
|
-
"$CLAUDE_DIR/omg-runtime/registry"
|
|
274
|
-
)
|
|
275
|
-
local dir
|
|
276
|
-
local file
|
|
277
|
-
for dir in "${dirs[@]}"; do
|
|
278
|
-
[ -d "$dir" ] || continue
|
|
279
|
-
while IFS= read -r file; do
|
|
280
|
-
if $DRY_RUN; then
|
|
281
|
-
say "DRY RUN: remove legacy $file"
|
|
282
|
-
else
|
|
283
|
-
rm -f "$file"
|
|
284
|
-
fi
|
|
285
|
-
done < <(find "$dir" -maxdepth 1 -type f -name "*.${legacy_ext}" | sort)
|
|
286
|
-
done
|
|
406
|
+
write_omg_manifest() {
|
|
407
|
+
if ! $DRY_RUN; then
|
|
408
|
+
printf '%s\n' "${NEW_MANIFEST_ENTRIES[@]}" | sort > "$OMG_MANIFEST"
|
|
409
|
+
fi
|
|
287
410
|
}
|
|
288
411
|
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
412
|
+
prune_plugin_mcp_from_settings() {
|
|
413
|
+
local mcp_path="$CLAUDE_DIR/.mcp.json"
|
|
414
|
+
if [ ! -f "$mcp_path" ]; then
|
|
415
|
+
return 0
|
|
416
|
+
fi
|
|
417
|
+
python3 - "$mcp_path" <<'PY'
|
|
418
|
+
import json
|
|
419
|
+
import sys
|
|
420
|
+
from pathlib import Path
|
|
421
|
+
|
|
422
|
+
path = Path(sys.argv[1])
|
|
423
|
+
try:
|
|
424
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
425
|
+
except Exception:
|
|
426
|
+
print("0")
|
|
427
|
+
raise SystemExit(0)
|
|
428
|
+
|
|
429
|
+
servers = data.get("mcpServers")
|
|
430
|
+
if not isinstance(servers, dict):
|
|
431
|
+
print("0")
|
|
432
|
+
raise SystemExit(0)
|
|
433
|
+
|
|
434
|
+
removed = 0
|
|
435
|
+
for key in ("context7", "filesystem", "websearch", "chrome-devtools"):
|
|
436
|
+
if key in servers:
|
|
437
|
+
servers.pop(key, None)
|
|
438
|
+
removed += 1
|
|
439
|
+
|
|
440
|
+
if removed:
|
|
441
|
+
path.write_text(json.dumps(data, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
442
|
+
|
|
443
|
+
print(str(removed))
|
|
444
|
+
PY
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
merge_plugin_mcp_into_settings() {
|
|
448
|
+
local mcp_path="$CLAUDE_DIR/.mcp.json"
|
|
449
|
+
local source_mcp_path="$SCRIPT_DIR/.mcp.json"
|
|
450
|
+
python3 - "$mcp_path" "$source_mcp_path" <<'PY'
|
|
451
|
+
import json
|
|
452
|
+
import sys
|
|
453
|
+
from pathlib import Path
|
|
454
|
+
|
|
455
|
+
mcp_path = Path(sys.argv[1])
|
|
456
|
+
source_mcp_path = Path(sys.argv[2])
|
|
457
|
+
|
|
458
|
+
mcp_config = {}
|
|
459
|
+
if mcp_path.exists():
|
|
460
|
+
try:
|
|
461
|
+
mcp_config = json.loads(mcp_path.read_text(encoding="utf-8"))
|
|
462
|
+
except Exception:
|
|
463
|
+
mcp_config = {}
|
|
464
|
+
if not isinstance(mcp_config, dict):
|
|
465
|
+
mcp_config = {}
|
|
466
|
+
|
|
467
|
+
try:
|
|
468
|
+
source_mcp = json.loads(source_mcp_path.read_text(encoding="utf-8"))
|
|
469
|
+
except Exception:
|
|
470
|
+
source_mcp = {}
|
|
471
|
+
|
|
472
|
+
incoming = source_mcp.get("mcpServers") if isinstance(source_mcp, dict) else {}
|
|
473
|
+
if not isinstance(incoming, dict):
|
|
474
|
+
incoming = {}
|
|
475
|
+
|
|
476
|
+
servers = mcp_config.get("mcpServers")
|
|
477
|
+
if not isinstance(servers, dict):
|
|
478
|
+
servers = {}
|
|
479
|
+
for key, value in incoming.items():
|
|
480
|
+
servers[key] = value
|
|
481
|
+
mcp_config["mcpServers"] = servers
|
|
482
|
+
|
|
483
|
+
mcp_path.parent.mkdir(parents=True, exist_ok=True)
|
|
484
|
+
mcp_path.write_text(json.dumps(mcp_config, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
485
|
+
print(str(len(incoming)))
|
|
486
|
+
PY
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
write_plugin_mcp_file() {
|
|
490
|
+
local target_path="$1"
|
|
491
|
+
local source_mcp_path="$SCRIPT_DIR/.mcp.json"
|
|
492
|
+
python3 - "$target_path" "$source_mcp_path" <<'PY'
|
|
493
|
+
import json
|
|
494
|
+
import sys
|
|
495
|
+
from pathlib import Path
|
|
496
|
+
|
|
497
|
+
target_path = Path(sys.argv[1])
|
|
498
|
+
source_mcp_path = Path(sys.argv[2])
|
|
499
|
+
|
|
500
|
+
try:
|
|
501
|
+
source_mcp = json.loads(source_mcp_path.read_text(encoding="utf-8"))
|
|
502
|
+
except Exception:
|
|
503
|
+
source_mcp = {}
|
|
504
|
+
|
|
505
|
+
mcp_servers = source_mcp.get("mcpServers") if isinstance(source_mcp, dict) else {}
|
|
506
|
+
if not isinstance(mcp_servers, dict):
|
|
507
|
+
mcp_servers = {}
|
|
508
|
+
|
|
509
|
+
payload = {"mcpServers": mcp_servers}
|
|
510
|
+
target_path.parent.mkdir(parents=True, exist_ok=True)
|
|
511
|
+
target_path.write_text(json.dumps(payload, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
512
|
+
print(str(len(mcp_servers)))
|
|
513
|
+
PY
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
register_plugin_in_registry() {
|
|
517
|
+
local plugin_ref="$1"
|
|
518
|
+
local install_path="$2"
|
|
519
|
+
local version="$3"
|
|
520
|
+
local settings_path="$CLAUDE_DIR/settings.json"
|
|
521
|
+
local installed_plugins_path="$CLAUDE_DIR/plugins/installed_plugins.json"
|
|
522
|
+
|
|
523
|
+
python3 - "$settings_path" "$installed_plugins_path" "$plugin_ref" "$install_path" "$version" <<'PY'
|
|
524
|
+
import json
|
|
525
|
+
import sys
|
|
526
|
+
from pathlib import Path
|
|
527
|
+
from datetime import datetime, timezone
|
|
528
|
+
|
|
529
|
+
settings_path = Path(sys.argv[1])
|
|
530
|
+
installed_plugins_path = Path(sys.argv[2])
|
|
531
|
+
plugin_ref = sys.argv[3]
|
|
532
|
+
install_path = sys.argv[4]
|
|
533
|
+
version = sys.argv[5]
|
|
534
|
+
now = datetime.now(timezone.utc).isoformat()
|
|
535
|
+
|
|
536
|
+
settings = {}
|
|
537
|
+
if settings_path.exists():
|
|
538
|
+
try:
|
|
539
|
+
settings = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
540
|
+
except Exception:
|
|
541
|
+
settings = {}
|
|
542
|
+
if not isinstance(settings, dict):
|
|
543
|
+
settings = {}
|
|
544
|
+
enabled = settings.get("enabledPlugins")
|
|
545
|
+
if not isinstance(enabled, dict):
|
|
546
|
+
enabled = {}
|
|
547
|
+
enabled[plugin_ref] = True
|
|
548
|
+
settings["enabledPlugins"] = enabled
|
|
549
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
550
|
+
settings_path.write_text(json.dumps(settings, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
551
|
+
|
|
552
|
+
installed = {}
|
|
553
|
+
if installed_plugins_path.exists():
|
|
554
|
+
try:
|
|
555
|
+
installed = json.loads(installed_plugins_path.read_text(encoding="utf-8"))
|
|
556
|
+
except Exception:
|
|
557
|
+
installed = {}
|
|
558
|
+
if not isinstance(installed, dict):
|
|
559
|
+
installed = {}
|
|
560
|
+
installed["version"] = 2
|
|
561
|
+
plugins = installed.get("plugins")
|
|
562
|
+
if not isinstance(plugins, dict):
|
|
563
|
+
plugins = {}
|
|
564
|
+
plugins[plugin_ref] = [{
|
|
565
|
+
"scope": "user",
|
|
566
|
+
"installPath": install_path,
|
|
567
|
+
"version": version,
|
|
568
|
+
"installedAt": now,
|
|
569
|
+
"lastUpdated": now,
|
|
570
|
+
}]
|
|
571
|
+
installed["plugins"] = plugins
|
|
572
|
+
installed_plugins_path.parent.mkdir(parents=True, exist_ok=True)
|
|
573
|
+
installed_plugins_path.write_text(json.dumps(installed, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
574
|
+
PY
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
unregister_plugin_from_registry() {
|
|
578
|
+
local plugin_ref="$1"
|
|
579
|
+
local settings_path="$CLAUDE_DIR/settings.json"
|
|
580
|
+
local installed_plugins_path="$CLAUDE_DIR/plugins/installed_plugins.json"
|
|
581
|
+
|
|
582
|
+
python3 - "$settings_path" "$installed_plugins_path" "$plugin_ref" <<'PY'
|
|
583
|
+
import json
|
|
584
|
+
import sys
|
|
585
|
+
from pathlib import Path
|
|
586
|
+
|
|
587
|
+
settings_path = Path(sys.argv[1])
|
|
588
|
+
installed_plugins_path = Path(sys.argv[2])
|
|
589
|
+
plugin_ref = sys.argv[3]
|
|
590
|
+
|
|
591
|
+
if settings_path.exists():
|
|
592
|
+
try:
|
|
593
|
+
settings = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
594
|
+
except Exception:
|
|
595
|
+
settings = {}
|
|
596
|
+
if isinstance(settings, dict):
|
|
597
|
+
enabled = settings.get("enabledPlugins")
|
|
598
|
+
if isinstance(enabled, dict) and plugin_ref in enabled:
|
|
599
|
+
enabled.pop(plugin_ref, None)
|
|
600
|
+
settings["enabledPlugins"] = enabled
|
|
601
|
+
settings_path.write_text(json.dumps(settings, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
602
|
+
|
|
603
|
+
if installed_plugins_path.exists():
|
|
604
|
+
try:
|
|
605
|
+
installed = json.loads(installed_plugins_path.read_text(encoding="utf-8"))
|
|
606
|
+
except Exception:
|
|
607
|
+
installed = {}
|
|
608
|
+
if isinstance(installed, dict):
|
|
609
|
+
plugins = installed.get("plugins")
|
|
610
|
+
if isinstance(plugins, dict) and plugin_ref in plugins:
|
|
611
|
+
plugins.pop(plugin_ref, None)
|
|
612
|
+
installed["plugins"] = plugins
|
|
613
|
+
installed_plugins_path.write_text(json.dumps(installed, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
614
|
+
PY
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
apply_omg_preset_to_settings() {
|
|
618
|
+
local settings_path="$1"
|
|
619
|
+
local preset="$2"
|
|
620
|
+
|
|
621
|
+
if [ ! -f "$settings_path" ]; then
|
|
622
|
+
return 0
|
|
623
|
+
fi
|
|
624
|
+
|
|
625
|
+
python3 - "$SCRIPT_DIR" "$settings_path" "$preset" <<'PY'
|
|
626
|
+
import json
|
|
627
|
+
import sys
|
|
628
|
+
from pathlib import Path
|
|
629
|
+
|
|
630
|
+
root = Path(sys.argv[1])
|
|
631
|
+
settings_path = Path(sys.argv[2])
|
|
632
|
+
preset = sys.argv[3]
|
|
633
|
+
|
|
634
|
+
sys.path.insert(0, str(root))
|
|
635
|
+
|
|
636
|
+
from runtime.adoption import CANONICAL_VERSION, get_preset_features, resolve_preset
|
|
637
|
+
|
|
638
|
+
try:
|
|
639
|
+
settings = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
640
|
+
except Exception:
|
|
641
|
+
settings = {}
|
|
642
|
+
|
|
643
|
+
if not isinstance(settings, dict):
|
|
644
|
+
settings = {}
|
|
645
|
+
|
|
646
|
+
omg = settings.get("_omg")
|
|
647
|
+
if not isinstance(omg, dict):
|
|
648
|
+
omg = {}
|
|
649
|
+
|
|
650
|
+
features = omg.get("features")
|
|
651
|
+
if not isinstance(features, dict):
|
|
652
|
+
features = {}
|
|
653
|
+
|
|
654
|
+
features.update(get_preset_features(resolve_preset(preset)))
|
|
655
|
+
omg["features"] = features
|
|
656
|
+
omg["preset"] = resolve_preset(preset)
|
|
657
|
+
omg["_version"] = CANONICAL_VERSION
|
|
658
|
+
settings["_omg"] = omg
|
|
659
|
+
|
|
660
|
+
settings_path.parent.mkdir(parents=True, exist_ok=True)
|
|
661
|
+
settings_path.write_text(json.dumps(settings, indent=2, ensure_ascii=True) + "\n", encoding="utf-8")
|
|
662
|
+
PY
|
|
328
663
|
}
|
|
329
664
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
665
|
+
write_native_adoption_report() {
|
|
666
|
+
python3 - "$SCRIPT_DIR" "$CLAUDE_DIR" "$ADOPTION_MODE" "$ADOPT_MODE" "$OMG_PRESET" <<'PY'
|
|
667
|
+
import sys
|
|
668
|
+
from pathlib import Path
|
|
669
|
+
|
|
670
|
+
root = Path(sys.argv[1])
|
|
671
|
+
project_dir = Path(sys.argv[2])
|
|
672
|
+
mode = sys.argv[3]
|
|
673
|
+
adopt = sys.argv[4]
|
|
674
|
+
preset = sys.argv[5]
|
|
675
|
+
|
|
676
|
+
sys.path.insert(0, str(root))
|
|
677
|
+
|
|
678
|
+
from runtime.adoption import build_adoption_report, write_adoption_report
|
|
679
|
+
|
|
680
|
+
report = build_adoption_report(project_dir, requested_mode=mode, preset=preset, adopt=adopt)
|
|
681
|
+
path = write_adoption_report(project_dir, report)
|
|
682
|
+
print(path)
|
|
683
|
+
PY
|
|
684
|
+
}
|
|
685
|
+
|
|
686
|
+
apply_adoption_mode_marker() {
|
|
687
|
+
if [ "$ADOPTION_MODE" = "coexist" ]; then
|
|
688
|
+
if ! $DRY_RUN; then
|
|
689
|
+
mkdir -p "$CLAUDE_DIR/hooks"
|
|
690
|
+
printf '%s\n' "coexist" > "$CLAUDE_DIR/hooks/.omg-coexist"
|
|
691
|
+
fi
|
|
692
|
+
track_file "hooks/.omg-coexist"
|
|
693
|
+
else
|
|
694
|
+
if ! $DRY_RUN; then
|
|
695
|
+
rm -f "$CLAUDE_DIR/hooks/.omg-coexist"
|
|
696
|
+
fi
|
|
697
|
+
fi
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
remove_omg_files() {
|
|
701
|
+
if ! $DRY_RUN; then
|
|
702
|
+
local plugin_bundle_marker="$PLUGIN_CACHE_DIR/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
703
|
+
local plugin_bundle_marker_legacy="$LEGACY_PLUGIN_CACHE_DIR/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
704
|
+
local remove_plugin_managed_mcp=false
|
|
705
|
+
if [ -f "$plugin_bundle_marker" ] || [ -f "$plugin_bundle_marker_legacy" ]; then
|
|
706
|
+
remove_plugin_managed_mcp=true
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
# Use manifest for precise removal if available.
|
|
710
|
+
if [ -f "$OMG_MANIFEST" ]; then
|
|
711
|
+
while IFS= read -r entry; do
|
|
712
|
+
[ -n "$entry" ] || continue
|
|
713
|
+
[[ "$entry" == "#"* ]] && continue
|
|
714
|
+
rm -f "$CLAUDE_DIR/$entry"
|
|
715
|
+
done < "$OMG_MANIFEST"
|
|
716
|
+
rm -f "$OMG_MANIFEST"
|
|
717
|
+
fi
|
|
718
|
+
|
|
719
|
+
# Also remove by pattern (covers pre-manifest installs + compat aliases).
|
|
720
|
+
build_omg_hooks_list
|
|
721
|
+
for h in "${OMG_HOOKS[@]}"; do
|
|
722
|
+
rm -f "$CLAUDE_DIR/hooks/$h"
|
|
723
|
+
done
|
|
724
|
+
rm -f "$CLAUDE_DIR/hooks/.omg-version" "$CLAUDE_DIR/hooks/.omg-coexist"
|
|
725
|
+
|
|
726
|
+
# Remove OMG rules and old v3 rule set.
|
|
727
|
+
for r in "$CLAUDE_DIR"/rules/0[0-4]-*.md; do
|
|
728
|
+
[ -f "$r" ] && rm "$r"
|
|
729
|
+
done
|
|
730
|
+
for rule in "${V3_RULES[@]}"; do
|
|
731
|
+
rm -f "$CLAUDE_DIR/rules/$rule"
|
|
732
|
+
done
|
|
733
|
+
|
|
734
|
+
# Remove OMG agents, commands, templates.
|
|
735
|
+
rm -f "$CLAUDE_DIR"/agents/omg-*.md
|
|
736
|
+
if [ -d "$CLAUDE_DIR/commands" ]; then
|
|
737
|
+
while IFS= read -r cmd_path; do
|
|
738
|
+
if [ -n "$cmd_path" ] && is_omg_managed_command_file "$cmd_path"; then
|
|
739
|
+
rm -f "$cmd_path"
|
|
740
|
+
fi
|
|
741
|
+
done < <(find "$CLAUDE_DIR/commands" -maxdepth 1 -type f -name "*.md" 2>/dev/null | sort)
|
|
742
|
+
fi
|
|
743
|
+
[[ "$CLAUDE_DIR/templates/omg" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $CLAUDE_DIR/templates/omg" >&2; exit 1; }
|
|
744
|
+
rm -rf "$CLAUDE_DIR/templates/omg"
|
|
745
|
+
[[ "$CLAUDE_DIR/omg-runtime" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $CLAUDE_DIR/omg-runtime" >&2; exit 1; }
|
|
746
|
+
rm -rf "$CLAUDE_DIR/omg-runtime"
|
|
747
|
+
|
|
748
|
+
[[ "$PLUGIN_CACHE_DIR" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $PLUGIN_CACHE_DIR" >&2; exit 1; }
|
|
749
|
+
rm -rf "$PLUGIN_CACHE_DIR"
|
|
750
|
+
[[ "$LEGACY_PLUGIN_CACHE_DIR" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $LEGACY_PLUGIN_CACHE_DIR" >&2; exit 1; }
|
|
751
|
+
rm -rf "$LEGACY_PLUGIN_CACHE_DIR"
|
|
752
|
+
rm -f "$CLAUDE_DIR/hud/omg-hud.mjs"
|
|
753
|
+
unregister_plugin_from_registry "$PLUGIN_REF"
|
|
754
|
+
unregister_plugin_from_registry "$LEGACY_PLUGIN_REF"
|
|
755
|
+
|
|
756
|
+
if $remove_plugin_managed_mcp; then
|
|
757
|
+
local pruned_mcp=0
|
|
758
|
+
pruned_mcp=$(prune_plugin_mcp_from_settings)
|
|
759
|
+
if [ "${pruned_mcp:-0}" -gt 0 ]; then
|
|
760
|
+
echo " ✓ Plugin-managed MCP servers removed from .mcp.json ($pruned_mcp)"
|
|
761
|
+
fi
|
|
762
|
+
fi
|
|
763
|
+
fi
|
|
335
764
|
}
|
|
336
765
|
|
|
337
766
|
install_plugin_bundle() {
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
767
|
+
local plugin_ref="$PLUGIN_REF"
|
|
768
|
+
local plugin_root="$PLUGIN_CACHE_DIR/$VERSION"
|
|
769
|
+
local plugin_manifest_src="$SCRIPT_DIR/.claude-plugin/plugin.json"
|
|
770
|
+
local plugin_manifest_target="$plugin_root/.claude-plugin/plugin.json"
|
|
771
|
+
local plugin_mcp_target="$plugin_root/.mcp.json"
|
|
772
|
+
local hud_src="$SCRIPT_DIR/hud/omg-hud.mjs"
|
|
773
|
+
local hud_target="$CLAUDE_DIR/hud/omg-hud.mjs"
|
|
774
|
+
|
|
775
|
+
echo " Plugin bundle mode enabled: install plugin + MCP + HUD together"
|
|
776
|
+
if $DRY_RUN; then
|
|
777
|
+
echo " (would install plugin bundle under $plugin_root and deploy HUD to $hud_target)"
|
|
778
|
+
echo " (would register plugin in ~/.claude/plugins/installed_plugins.json and enable it in settings.json)"
|
|
779
|
+
echo " (would merge plugin MCP servers into .mcp.json)"
|
|
780
|
+
return 0
|
|
781
|
+
fi
|
|
782
|
+
|
|
783
|
+
mkdir -p "$plugin_root/.claude-plugin"
|
|
784
|
+
mkdir -p "$CLAUDE_DIR/hud"
|
|
785
|
+
cp "$plugin_manifest_src" "$plugin_manifest_target"
|
|
786
|
+
|
|
787
|
+
# Provide a fallback .mcp.json if not shipped in npm package
|
|
788
|
+
if [ ! -f "$SCRIPT_DIR/.mcp.json" ]; then
|
|
789
|
+
local _fallback_mcp_dir
|
|
790
|
+
_fallback_mcp_dir=$(mktemp -d)
|
|
791
|
+
cat > "$_fallback_mcp_dir/.mcp.json" <<'FALLBACK_MCP'
|
|
792
|
+
{
|
|
793
|
+
"mcpServers": {
|
|
794
|
+
"context7": {
|
|
795
|
+
"command": "npx",
|
|
796
|
+
"args": ["@upstash/context7-mcp@2.1.3"]
|
|
797
|
+
},
|
|
798
|
+
"filesystem": {
|
|
799
|
+
"command": "npx",
|
|
800
|
+
"args": ["@modelcontextprotocol/server-filesystem@2026.1.14", "."]
|
|
801
|
+
},
|
|
802
|
+
"websearch": {
|
|
803
|
+
"command": "npx",
|
|
804
|
+
"args": ["@zhafron/mcp-web-search@1.2.2"]
|
|
805
|
+
},
|
|
806
|
+
"chrome-devtools": {
|
|
807
|
+
"command": "npx",
|
|
808
|
+
"args": ["chrome-devtools-mcp@0.19.0"]
|
|
809
|
+
}
|
|
810
|
+
}
|
|
351
811
|
}
|
|
812
|
+
FALLBACK_MCP
|
|
813
|
+
SCRIPT_DIR="$_fallback_mcp_dir" write_plugin_mcp_file "$plugin_mcp_target" >/dev/null
|
|
814
|
+
rm -rf "$_fallback_mcp_dir"
|
|
815
|
+
else
|
|
816
|
+
write_plugin_mcp_file "$plugin_mcp_target" >/dev/null
|
|
817
|
+
fi
|
|
818
|
+
|
|
819
|
+
cp "$hud_src" "$hud_target"
|
|
820
|
+
mkdir -p "$PLUGIN_CACHE_DIR"
|
|
821
|
+
printf '%s\n' "omg-plugin-bundle-v1" > "$PLUGIN_CACHE_DIR/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
822
|
+
|
|
823
|
+
unregister_plugin_from_registry "$LEGACY_PLUGIN_REF"
|
|
824
|
+
register_plugin_in_registry "$plugin_ref" "$plugin_root" "$VERSION"
|
|
825
|
+
merge_plugin_mcp_into_settings >/dev/null
|
|
352
826
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
copy_tree "$SCRIPT_DIR/templates" "$CLAUDE_DIR/templates/omg" "all"
|
|
359
|
-
copy_tree "$SCRIPT_DIR/hooks" "$CLAUDE_DIR/hooks" "ts-only"
|
|
360
|
-
copy_tree "$SCRIPT_DIR/runtime" "$CLAUDE_DIR/omg-runtime/runtime" "runtime"
|
|
361
|
-
copy_tree "$SCRIPT_DIR/scripts" "$CLAUDE_DIR/omg-runtime/scripts" "runtime"
|
|
362
|
-
copy_tree "$SCRIPT_DIR/tools" "$CLAUDE_DIR/omg-runtime/tools" "runtime"
|
|
363
|
-
copy_tree "$SCRIPT_DIR/control_plane" "$CLAUDE_DIR/omg-runtime/control_plane" "runtime"
|
|
364
|
-
copy_tree "$SCRIPT_DIR/lab" "$CLAUDE_DIR/omg-runtime/lab" "runtime"
|
|
365
|
-
copy_tree "$SCRIPT_DIR/registry" "$CLAUDE_DIR/omg-runtime/registry" "runtime"
|
|
366
|
-
copy_tree "$SCRIPT_DIR/omg_natives" "$CLAUDE_DIR/omg-runtime/omg_natives" "runtime"
|
|
367
|
-
copy_tree "$SCRIPT_DIR/hud" "$CLAUDE_DIR/omg-runtime/hud" "all"
|
|
368
|
-
copy_file "$SCRIPT_DIR/.mcp.json" "$CLAUDE_DIR/omg-runtime/.mcp.json" 644
|
|
369
|
-
copy_file "$SCRIPT_DIR/package.json" "$CLAUDE_DIR/omg-runtime/package.json" 644
|
|
370
|
-
copy_file "$SCRIPT_DIR/tsconfig.json" "$CLAUDE_DIR/omg-runtime/tsconfig.json" 644
|
|
371
|
-
copy_file "$SCRIPT_DIR/bunfig.toml" "$CLAUDE_DIR/omg-runtime/bunfig.toml" 644
|
|
827
|
+
track_file "plugins/cache/$PLUGIN_MARKETPLACE/$PLUGIN_NAME/$VERSION/.claude-plugin/plugin.json"
|
|
828
|
+
track_file "plugins/cache/$PLUGIN_MARKETPLACE/$PLUGIN_NAME/$VERSION/.mcp.json"
|
|
829
|
+
track_file "plugins/cache/$PLUGIN_MARKETPLACE/$PLUGIN_NAME/$PLUGIN_BUNDLE_MARKER_FILE"
|
|
830
|
+
track_file "hud/omg-hud.mjs"
|
|
831
|
+
echo " ✓ Plugin bundle installed and registered in Claude plugin settings"
|
|
372
832
|
}
|
|
373
833
|
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
834
|
+
run_uninstall() {
|
|
835
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
836
|
+
echo " OMG Setup Manager — uninstall"
|
|
837
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
838
|
+
echo ""
|
|
839
|
+
|
|
840
|
+
if $DRY_RUN; then
|
|
841
|
+
echo " *** DRY RUN — no files will be changed ***"
|
|
842
|
+
echo ""
|
|
843
|
+
fi
|
|
844
|
+
|
|
845
|
+
preflight
|
|
846
|
+
|
|
847
|
+
local existing_ver=""
|
|
848
|
+
if [ -f "$CLAUDE_DIR/hooks/.omg-version" ]; then
|
|
849
|
+
existing_ver=$(cat "$CLAUDE_DIR/hooks/.omg-version" 2>/dev/null || echo "")
|
|
850
|
+
fi
|
|
851
|
+
if [ -n "$existing_ver" ]; then
|
|
852
|
+
echo " ✓ Existing OMG install: $existing_ver"
|
|
853
|
+
else
|
|
854
|
+
echo " ~ No .omg-version marker found; uninstall will still remove known OMG files."
|
|
855
|
+
fi
|
|
856
|
+
|
|
857
|
+
if ! $NON_INTERACTIVE && ! $DRY_RUN; then
|
|
858
|
+
read -p "Proceed with uninstall? [y/N] " -n 1 -r
|
|
859
|
+
echo ""
|
|
860
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
861
|
+
echo "Cancelled."
|
|
862
|
+
exit 0
|
|
863
|
+
fi
|
|
864
|
+
fi
|
|
865
|
+
|
|
866
|
+
echo ""
|
|
867
|
+
echo "Uninstall: removing OMG-managed files from $CLAUDE_DIR"
|
|
868
|
+
ensure_backup
|
|
869
|
+
if ! $DRY_RUN; then
|
|
870
|
+
echo " ✓ Backup: $BACKUP_DIR"
|
|
871
|
+
else
|
|
872
|
+
echo " (would backup to $BACKUP_DIR)"
|
|
873
|
+
fi
|
|
874
|
+
remove_omg_files
|
|
875
|
+
if $DRY_RUN; then
|
|
876
|
+
echo " (would remove OMG hooks/rules/agents/commands/templates)"
|
|
877
|
+
else
|
|
878
|
+
echo " ✓ Removed OMG hooks/rules/agents/commands/templates"
|
|
879
|
+
fi
|
|
880
|
+
|
|
881
|
+
echo ""
|
|
882
|
+
echo "Uninstall complete."
|
|
883
|
+
echo " ✓ If plugin bundle was installed, plugin + MCP + HUD were removed together"
|
|
884
|
+
echo "Preserved:"
|
|
885
|
+
echo " - $CLAUDE_DIR/settings.json"
|
|
886
|
+
echo " - project .omg/ data"
|
|
887
|
+
echo " - non-OMG custom files"
|
|
402
888
|
}
|
|
403
889
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
890
|
+
run_install_like() {
|
|
891
|
+
local existing_ver=""
|
|
892
|
+
local removed=0
|
|
893
|
+
local installed_rules=0
|
|
894
|
+
local installed_hooks=0
|
|
895
|
+
local hook_errors=0
|
|
896
|
+
local installed_agents=0
|
|
897
|
+
local installed_cmds=0
|
|
898
|
+
|
|
899
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
900
|
+
echo " OMG Setup Manager — $ACTION"
|
|
901
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
902
|
+
echo ""
|
|
903
|
+
|
|
904
|
+
if $DRY_RUN; then
|
|
905
|
+
echo " *** DRY RUN — no files will be changed ***"
|
|
906
|
+
echo ""
|
|
907
|
+
fi
|
|
908
|
+
|
|
909
|
+
preflight
|
|
910
|
+
|
|
911
|
+
if [ -f "$CLAUDE_DIR/hooks/.omg-version" ]; then
|
|
912
|
+
existing_ver=$(cat "$CLAUDE_DIR/hooks/.omg-version" 2>/dev/null || echo "")
|
|
913
|
+
fi
|
|
914
|
+
|
|
915
|
+
if [ "$ACTION" = "update" ] && [ -z "$existing_ver" ]; then
|
|
916
|
+
echo " ~ No existing OMG install detected. update will proceed as install."
|
|
917
|
+
fi
|
|
918
|
+
|
|
919
|
+
if [ -n "$existing_ver" ]; then
|
|
920
|
+
echo " ✓ Existing: $existing_ver → target $VERSION"
|
|
921
|
+
else
|
|
922
|
+
echo " ✓ Fresh install"
|
|
923
|
+
fi
|
|
924
|
+
echo " ✓ Command surface: /OMG:setup and /OMG:crazy are the primary native front door"
|
|
925
|
+
echo " ✓ Adoption mode: $ADOPTION_MODE"
|
|
926
|
+
echo " ✓ Preset: $OMG_PRESET"
|
|
927
|
+
|
|
928
|
+
if $FRESH_INSTALL; then
|
|
929
|
+
echo ""
|
|
930
|
+
echo "Fresh/reinstall mode: remove OMG files before install."
|
|
931
|
+
if ! $NON_INTERACTIVE && ! $DRY_RUN; then
|
|
932
|
+
read -p "Proceed with fresh cleanup? [y/N] " -n 1 -r
|
|
933
|
+
echo ""
|
|
934
|
+
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
|
935
|
+
echo "Cancelled."
|
|
936
|
+
exit 0
|
|
937
|
+
fi
|
|
938
|
+
fi
|
|
939
|
+
|
|
940
|
+
ensure_backup
|
|
941
|
+
if ! $DRY_RUN; then
|
|
942
|
+
echo " ✓ Backup: $BACKUP_DIR"
|
|
943
|
+
else
|
|
944
|
+
echo " (would backup to $BACKUP_DIR)"
|
|
945
|
+
fi
|
|
946
|
+
remove_omg_files
|
|
947
|
+
if $DRY_RUN; then
|
|
948
|
+
echo " (would remove OMG hooks/rules/agents/commands/templates)"
|
|
949
|
+
else
|
|
950
|
+
echo " ✓ Clean slate ready"
|
|
951
|
+
fi
|
|
952
|
+
existing_ver=""
|
|
953
|
+
fi
|
|
954
|
+
|
|
955
|
+
if [ -n "$existing_ver" ] && ! $FRESH_INSTALL; then
|
|
956
|
+
echo ""
|
|
957
|
+
echo "Step 0/7: Backup existing installation..."
|
|
958
|
+
ensure_backup
|
|
959
|
+
if ! $DRY_RUN; then
|
|
960
|
+
echo " ✓ Backup: $BACKUP_DIR"
|
|
961
|
+
else
|
|
962
|
+
echo " (would backup to $BACKUP_DIR)"
|
|
963
|
+
fi
|
|
964
|
+
fi
|
|
965
|
+
|
|
966
|
+
echo ""
|
|
967
|
+
echo "Step 1/7: Remove deprecated files..."
|
|
968
|
+
|
|
969
|
+
for rule in "${V3_RULES[@]}"; do
|
|
970
|
+
target="$CLAUDE_DIR/rules/$rule"
|
|
971
|
+
if [ -f "$target" ]; then
|
|
972
|
+
! $DRY_RUN && rm "$target"
|
|
973
|
+
echo " - rules/$rule"
|
|
974
|
+
removed=$((removed + 1))
|
|
975
|
+
fi
|
|
976
|
+
done
|
|
977
|
+
|
|
978
|
+
for agent in "${V3_AGENTS_REMOVE[@]}"; do
|
|
979
|
+
target="$CLAUDE_DIR/agents/$agent"
|
|
980
|
+
if [ -f "$target" ]; then
|
|
981
|
+
! $DRY_RUN && rm "$target"
|
|
982
|
+
echo " - agents/$agent (v3 deprecated)"
|
|
983
|
+
removed=$((removed + 1))
|
|
984
|
+
fi
|
|
985
|
+
done
|
|
986
|
+
|
|
987
|
+
for agent in "${OLD_OMG_AGENTS[@]}"; do
|
|
988
|
+
target="$CLAUDE_DIR/agents/$agent"
|
|
989
|
+
if [ -f "$target" ]; then
|
|
990
|
+
if grep -q "OMG\|omg\|circuit.breaker\|escalat" "$target" 2>/dev/null; then
|
|
991
|
+
! $DRY_RUN && rm "$target"
|
|
992
|
+
echo " - agents/$agent (renamed to omg-$agent)"
|
|
993
|
+
removed=$((removed + 1))
|
|
994
|
+
else
|
|
995
|
+
echo " ~ agents/$agent (kept — appears to be non-OMG/custom)"
|
|
996
|
+
fi
|
|
997
|
+
fi
|
|
998
|
+
done
|
|
999
|
+
|
|
1000
|
+
for cmd in "${V3_COMMANDS_REMOVE[@]}" "${V4_COMMANDS_REMOVE[@]}"; do
|
|
1001
|
+
target="$CLAUDE_DIR/commands/$cmd"
|
|
1002
|
+
if [ -f "$target" ]; then
|
|
1003
|
+
if is_omg_managed_command_file "$target"; then
|
|
1004
|
+
! $DRY_RUN && rm "$target"
|
|
1005
|
+
echo " - commands/$cmd (v4 → OMG:$cmd)"
|
|
1006
|
+
removed=$((removed + 1))
|
|
1007
|
+
else
|
|
1008
|
+
echo " ~ commands/$cmd (kept — appears to be non-OMG/custom)"
|
|
1009
|
+
fi
|
|
1010
|
+
fi
|
|
1011
|
+
done
|
|
1012
|
+
|
|
1013
|
+
if compgen -G "$CLAUDE_DIR/commands/*omc*.md" > /dev/null; then
|
|
1014
|
+
for cmd in "$CLAUDE_DIR"/commands/*omc*.md; do
|
|
1015
|
+
[ -f "$cmd" ] || continue
|
|
1016
|
+
if is_omg_managed_command_file "$cmd"; then
|
|
1017
|
+
! $DRY_RUN && rm "$cmd"
|
|
1018
|
+
echo " - commands/$(basename "$cmd") (removed legacy command)"
|
|
1019
|
+
removed=$((removed + 1))
|
|
1020
|
+
fi
|
|
1021
|
+
done
|
|
1022
|
+
fi
|
|
1023
|
+
|
|
1024
|
+
if [ -d "$CLAUDE_DIR/hooks/__pycache__" ]; then
|
|
1025
|
+
! $DRY_RUN && rm -rf "$CLAUDE_DIR/hooks/__pycache__"
|
|
1026
|
+
removed=$((removed + 1))
|
|
1027
|
+
fi
|
|
1028
|
+
! $DRY_RUN && find "$CLAUDE_DIR/hooks/" -name "*.pyc" -delete 2>/dev/null || true
|
|
1029
|
+
[ $removed -eq 0 ] && echo " (nothing to remove)" || echo " ✓ Removed $removed deprecated files"
|
|
1030
|
+
|
|
1031
|
+
echo ""
|
|
1032
|
+
echo "Step 2/7: Core Rules → $CLAUDE_DIR/rules/"
|
|
1033
|
+
! $DRY_RUN && mkdir -p "$CLAUDE_DIR/rules"
|
|
1034
|
+
for f in "$SCRIPT_DIR"/rules/core/*.md; do
|
|
1035
|
+
name=$(basename "$f")
|
|
1036
|
+
target="$CLAUDE_DIR/rules/$name"
|
|
1037
|
+
! $DRY_RUN && cp "$f" "$target"
|
|
1038
|
+
installed_rules=$((installed_rules + 1))
|
|
1039
|
+
track_file "rules/$name"
|
|
1040
|
+
done
|
|
1041
|
+
echo " ✓ $installed_rules core rules"
|
|
1042
|
+
|
|
1043
|
+
echo ""
|
|
1044
|
+
echo "Step 3/7: Hooks → $CLAUDE_DIR/hooks/"
|
|
1045
|
+
! $DRY_RUN && mkdir -p "$CLAUDE_DIR/hooks"
|
|
1046
|
+
for f in "$SCRIPT_DIR"/hooks/*.py; do
|
|
1047
|
+
name=$(basename "$f")
|
|
1048
|
+
target="$CLAUDE_DIR/hooks/$name"
|
|
1049
|
+
if ! $DRY_RUN; then
|
|
1050
|
+
install_file "$f" "$target"
|
|
1051
|
+
if ! $USE_SYMLINK; then
|
|
1052
|
+
chmod +x "$target"
|
|
1053
|
+
fi
|
|
1054
|
+
fi
|
|
1055
|
+
if python3 -c "import py_compile; py_compile.compile('$f', doraise=True)" 2>/dev/null; then
|
|
1056
|
+
echo " ✓ $name"
|
|
1057
|
+
else
|
|
1058
|
+
echo " ❌ $name (SYNTAX ERROR)"
|
|
1059
|
+
hook_errors=$((hook_errors + 1))
|
|
1060
|
+
ERRORS=$((ERRORS + 1))
|
|
1061
|
+
fi
|
|
1062
|
+
installed_hooks=$((installed_hooks + 1))
|
|
1063
|
+
track_file "hooks/$name"
|
|
1064
|
+
done
|
|
1065
|
+
! $DRY_RUN && echo "$VERSION" > "$CLAUDE_DIR/hooks/.omg-version"
|
|
1066
|
+
apply_adoption_mode_marker
|
|
1067
|
+
echo " ✓ $installed_hooks hooks ($hook_errors errors)"
|
|
1068
|
+
|
|
1069
|
+
echo ""
|
|
1070
|
+
echo "Step 4/7: Agents → $CLAUDE_DIR/agents/"
|
|
1071
|
+
! $DRY_RUN && mkdir -p "$CLAUDE_DIR/agents"
|
|
1072
|
+
for f in "$SCRIPT_DIR"/agents/*.md; do
|
|
1073
|
+
name=$(basename "$f")
|
|
1074
|
+
target="$CLAUDE_DIR/agents/$name"
|
|
1075
|
+
if ! $DRY_RUN; then
|
|
1076
|
+
[ -f "$target" ] && [ -z "$existing_ver" ] && cp "$target" "$target.bak.$BACKUP_TS"
|
|
1077
|
+
install_file "$f" "$target"
|
|
1078
|
+
fi
|
|
1079
|
+
echo " ✓ $name"
|
|
1080
|
+
installed_agents=$((installed_agents + 1))
|
|
1081
|
+
track_file "agents/$name"
|
|
1082
|
+
done
|
|
1083
|
+
echo " ✓ $installed_agents agents"
|
|
1084
|
+
|
|
1085
|
+
echo ""
|
|
1086
|
+
echo "Step 5/7: Commands → $CLAUDE_DIR/commands/"
|
|
1087
|
+
! $DRY_RUN && mkdir -p "$CLAUDE_DIR/commands"
|
|
1088
|
+
for f in "$SCRIPT_DIR"/commands/*.md; do
|
|
1089
|
+
name=$(basename "$f")
|
|
1090
|
+
if [[ "$name" == *omc* ]]; then
|
|
1091
|
+
echo " - /$(basename "$name" .md) (skipped: legacy alias commands are unsupported)"
|
|
1092
|
+
continue
|
|
1093
|
+
fi
|
|
1094
|
+
target="$CLAUDE_DIR/commands/$name"
|
|
1095
|
+
if [ -f "$target" ] && ! is_omg_managed_command_file "$target"; then
|
|
1096
|
+
echo " ~ /$(basename "$name" .md) (kept existing custom command)"
|
|
1097
|
+
continue
|
|
1098
|
+
fi
|
|
1099
|
+
if ! $DRY_RUN; then
|
|
1100
|
+
install_file "$f" "$target"
|
|
1101
|
+
if ! $USE_SYMLINK; then
|
|
1102
|
+
mark_omg_managed_command_file "$target"
|
|
1103
|
+
fi
|
|
1104
|
+
fi
|
|
1105
|
+
echo " ✓ /$(basename "$name" .md)"
|
|
1106
|
+
installed_cmds=$((installed_cmds + 1))
|
|
1107
|
+
track_file "commands/$name"
|
|
1108
|
+
done
|
|
1109
|
+
echo ""
|
|
1110
|
+
echo "Step 6/7: Settings + Templates..."
|
|
1111
|
+
MERGE="$SCRIPT_DIR/scripts/settings-merge.py"
|
|
1112
|
+
TARGET="$CLAUDE_DIR/settings.json"
|
|
1113
|
+
SOURCE="$SCRIPT_DIR/settings.json"
|
|
1114
|
+
if ! $DRY_RUN; then
|
|
1115
|
+
if [ ! -f "$TARGET" ]; then
|
|
1116
|
+
cp "$SOURCE" "$TARGET"
|
|
1117
|
+
apply_omg_preset_to_settings "$TARGET" "$OMG_PRESET"
|
|
1118
|
+
echo " ✓ Created settings.json"
|
|
1119
|
+
else
|
|
1120
|
+
if [ "$MERGE_POLICY" = "skip" ]; then
|
|
1121
|
+
apply_omg_preset_to_settings "$TARGET" "$OMG_PRESET"
|
|
1122
|
+
echo " ⊘ Skipped settings merge (--merge-policy=skip)"
|
|
1123
|
+
elif [ "$MERGE_POLICY" = "apply" ] || $NON_INTERACTIVE; then
|
|
1124
|
+
python3 "$MERGE" "$TARGET" "$SOURCE"
|
|
1125
|
+
apply_omg_preset_to_settings "$TARGET" "$OMG_PRESET"
|
|
1126
|
+
echo " ✓ Settings merged (auto)"
|
|
1127
|
+
else
|
|
1128
|
+
echo " Merging settings.json..."
|
|
1129
|
+
dry_run_preview="$(python3 "$MERGE" "$TARGET" "$SOURCE" --dry-run 2>&1)"
|
|
1130
|
+
printf '%s\n' "$dry_run_preview" | sed -n '1,5p' | sed 's/^/ /'
|
|
1131
|
+
echo ""
|
|
1132
|
+
if read -p " Apply merge? [Y/n] " -n 1 -r 2>/dev/null; then
|
|
1133
|
+
echo ""
|
|
1134
|
+
if [[ ! $REPLY =~ ^[Nn]$ ]]; then
|
|
1135
|
+
python3 "$MERGE" "$TARGET" "$SOURCE"
|
|
1136
|
+
apply_omg_preset_to_settings "$TARGET" "$OMG_PRESET"
|
|
1137
|
+
echo " ✓ Settings merged"
|
|
1138
|
+
else
|
|
1139
|
+
echo " ⊘ Skipped (manual merge needed)"
|
|
1140
|
+
fi
|
|
1141
|
+
else
|
|
1142
|
+
# read failed — only auto-apply if we can confirm non-interactive context
|
|
1143
|
+
# non-interactive fallback: check for clear non-interactive indicators
|
|
1144
|
+
if [ ! -t 0 ] || [ -n "${npm_lifecycle_event:-}" ] || [ -n "${npm_execpath:-}" ]; then
|
|
1145
|
+
python3 "$MERGE" "$TARGET" "$SOURCE"
|
|
1146
|
+
apply_omg_preset_to_settings "$TARGET" "$OMG_PRESET"
|
|
1147
|
+
echo " ✓ Settings merged (auto — non-interactive fallback)"
|
|
1148
|
+
else
|
|
1149
|
+
echo " ⚠ Could not read input. Skipping merge to be safe."
|
|
1150
|
+
echo " Run manually: ./OMG-setup.sh update --merge-policy=apply"
|
|
1151
|
+
fi
|
|
1152
|
+
fi
|
|
1153
|
+
fi
|
|
1154
|
+
fi
|
|
1155
|
+
|
|
1156
|
+
if $USE_SYMLINK; then
|
|
1157
|
+
# In symlink mode, link entire directories for templates
|
|
1158
|
+
if [ -e "$CLAUDE_DIR/templates/omg" ] || [ -L "$CLAUDE_DIR/templates/omg" ]; then
|
|
1159
|
+
rm -rf "$CLAUDE_DIR/templates/omg"
|
|
1160
|
+
fi
|
|
1161
|
+
ln -s "$SCRIPT_DIR/templates" "$CLAUDE_DIR/templates/omg"
|
|
1162
|
+
# Also link contextual rules
|
|
1163
|
+
mkdir -p "$CLAUDE_DIR/templates/omg/contextual-rules"
|
|
1164
|
+
for cr in "$SCRIPT_DIR"/rules/contextual/*.md; do
|
|
1165
|
+
[ -f "$cr" ] && track_file "templates/omg/contextual-rules/$(basename "$cr")"
|
|
1166
|
+
done
|
|
1167
|
+
else
|
|
1168
|
+
mkdir -p "$CLAUDE_DIR/templates/omg"
|
|
1169
|
+
cp "$SCRIPT_DIR"/templates/* "$CLAUDE_DIR/templates/omg/" 2>/dev/null || true
|
|
1170
|
+
for t in "$SCRIPT_DIR"/templates/*; do
|
|
1171
|
+
[ -f "$t" ] && track_file "templates/omg/$(basename "$t")"
|
|
1172
|
+
done
|
|
1173
|
+
mkdir -p "$CLAUDE_DIR/templates/omg/contextual-rules"
|
|
1174
|
+
cp "$SCRIPT_DIR"/rules/contextual/*.md "$CLAUDE_DIR/templates/omg/contextual-rules/" 2>/dev/null || true
|
|
1175
|
+
for cr in "$SCRIPT_DIR"/rules/contextual/*.md; do
|
|
1176
|
+
[ -f "$cr" ] && track_file "templates/omg/contextual-rules/$(basename "$cr")"
|
|
1177
|
+
done
|
|
1178
|
+
fi
|
|
1179
|
+
mkdir -p "$CLAUDE_DIR/templates/omg/state/memory"
|
|
1180
|
+
mkdir -p "$CLAUDE_DIR/templates/omg/state/learnings"
|
|
1181
|
+
mkdir -p "$CLAUDE_DIR/templates/omg/state/ledger"
|
|
1182
|
+
echo " \u2713 State directory templates (memory, learnings, ledger)"
|
|
1183
|
+
echo " \u2713 Templates + contextual rules"
|
|
1184
|
+
|
|
1185
|
+
if $USE_SYMLINK; then
|
|
1186
|
+
# In symlink mode, link runtime directories instead of copying
|
|
1187
|
+
mkdir -p "$CLAUDE_DIR/omg-runtime/scripts"
|
|
1188
|
+
install_file "$SCRIPT_DIR/scripts/omg.py" "$CLAUDE_DIR/omg-runtime/scripts/omg.py"
|
|
1189
|
+
|
|
1190
|
+
[[ "$CLAUDE_DIR/omg-runtime/runtime" == "$CLAUDE_DIR"* ]] || { echo "ERROR: symlink target outside expected directory: $CLAUDE_DIR/omg-runtime/runtime" >&2; exit 1; }
|
|
1191
|
+
[[ "$CLAUDE_DIR/omg-runtime/hooks" == "$CLAUDE_DIR"* ]] || { echo "ERROR: symlink target outside expected directory: $CLAUDE_DIR/omg-runtime/hooks" >&2; exit 1; }
|
|
1192
|
+
[[ "$CLAUDE_DIR/omg-runtime/lab" == "$CLAUDE_DIR"* ]] || { echo "ERROR: symlink target outside expected directory: $CLAUDE_DIR/omg-runtime/lab" >&2; exit 1; }
|
|
1193
|
+
|
|
1194
|
+
rm -rf "$CLAUDE_DIR/omg-runtime/runtime" "$CLAUDE_DIR/omg-runtime/hooks" "$CLAUDE_DIR/omg-runtime/lab"
|
|
1195
|
+
ln -s "$SCRIPT_DIR/runtime" "$CLAUDE_DIR/omg-runtime/runtime"
|
|
1196
|
+
ln -s "$SCRIPT_DIR/hooks" "$CLAUDE_DIR/omg-runtime/hooks"
|
|
1197
|
+
ln -s "$SCRIPT_DIR/lab" "$CLAUDE_DIR/omg-runtime/lab"
|
|
1198
|
+
|
|
1199
|
+
[[ "$CLAUDE_DIR/omg-runtime" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $CLAUDE_DIR/omg-runtime" >&2; exit 1; }
|
|
1200
|
+
find "$CLAUDE_DIR/omg-runtime" -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
|
|
1201
|
+
find "$CLAUDE_DIR/omg-runtime" -name "*.pyc" -delete 2>/dev/null || true
|
|
1202
|
+
echo " ✓ Portable runtime → $CLAUDE_DIR/omg-runtime (symlinked to $SCRIPT_DIR)"
|
|
1203
|
+
else
|
|
1204
|
+
mkdir -p "$CLAUDE_DIR/omg-runtime/scripts"
|
|
1205
|
+
cp "$SCRIPT_DIR/scripts/omg.py" "$CLAUDE_DIR/omg-runtime/scripts/omg.py"
|
|
1206
|
+
[[ "$CLAUDE_DIR/omg-runtime/runtime" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $CLAUDE_DIR/omg-runtime/runtime" >&2; exit 1; }
|
|
1207
|
+
rm -rf "$CLAUDE_DIR/omg-runtime/runtime" "$CLAUDE_DIR/omg-runtime/hooks" "$CLAUDE_DIR/omg-runtime/lab"
|
|
1208
|
+
cp -R "$SCRIPT_DIR/runtime" "$CLAUDE_DIR/omg-runtime/"
|
|
1209
|
+
cp -R "$SCRIPT_DIR/hooks" "$CLAUDE_DIR/omg-runtime/"
|
|
1210
|
+
cp -R "$SCRIPT_DIR/lab" "$CLAUDE_DIR/omg-runtime/"
|
|
1211
|
+
[[ "$CLAUDE_DIR/omg-runtime" == "$CLAUDE_DIR"* ]] || { echo "ERROR: rm -rf target outside expected directory: $CLAUDE_DIR/omg-runtime" >&2; exit 1; }
|
|
1212
|
+
find "$CLAUDE_DIR/omg-runtime" -name "__pycache__" -type d -exec rm -rf {} + 2>/dev/null || true
|
|
1213
|
+
find "$CLAUDE_DIR/omg-runtime" -name "*.pyc" -delete 2>/dev/null || true
|
|
1214
|
+
echo " ✓ Portable runtime → $CLAUDE_DIR/omg-runtime"
|
|
1215
|
+
fi
|
|
1216
|
+
if $INSTALL_AS_PLUGIN; then
|
|
1217
|
+
install_plugin_bundle
|
|
1218
|
+
fi
|
|
1219
|
+
|
|
1220
|
+
local adoption_report_path=""
|
|
1221
|
+
adoption_report_path=$(write_native_adoption_report)
|
|
1222
|
+
echo " ✓ Adoption report → $adoption_report_path"
|
|
1223
|
+
else
|
|
1224
|
+
echo " (would merge settings.json + copy templates + provision portable runtime)"
|
|
1225
|
+
if $INSTALL_AS_PLUGIN; then
|
|
1226
|
+
install_plugin_bundle
|
|
1227
|
+
fi
|
|
1228
|
+
echo " (would write adoption report and apply preset/mode markers)"
|
|
1229
|
+
fi
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
echo ""
|
|
1233
|
+
echo "Step 7/7: Reconcile stale files..."
|
|
1234
|
+
reconcile_stale_files
|
|
1235
|
+
write_omg_manifest
|
|
1236
|
+
|
|
1237
|
+
echo ""
|
|
1238
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
1239
|
+
if [ $ERRORS -eq 0 ]; then
|
|
1240
|
+
echo " ✅ OMG ${VERSION} ${ACTION} completed successfully"
|
|
1241
|
+
else
|
|
1242
|
+
echo " ⚠ OMG ${VERSION} ${ACTION} completed with $ERRORS error(s)"
|
|
1243
|
+
fi
|
|
1244
|
+
echo "═══════════════════════════════════════════════════════════════"
|
|
1245
|
+
echo ""
|
|
1246
|
+
echo " Files: $installed_rules rules, $installed_hooks hooks, $installed_agents agents, $installed_cmds commands"
|
|
1247
|
+
echo " Version: $VERSION"
|
|
1248
|
+
if $USE_SYMLINK; then
|
|
1249
|
+
echo " Mode: Symlink (live updates from $SCRIPT_DIR)"
|
|
1250
|
+
echo " Source: $SCRIPT_DIR"
|
|
1251
|
+
elif $FRESH_INSTALL; then
|
|
1252
|
+
echo " Mode: Fresh reinstall"
|
|
1253
|
+
elif [ -n "$existing_ver" ]; then
|
|
1254
|
+
echo " Upgraded: $existing_ver → $VERSION"
|
|
1255
|
+
echo " Backup: $BACKUP_DIR"
|
|
1256
|
+
fi
|
|
1257
|
+
echo ""
|
|
1258
|
+
if $DRY_RUN; then
|
|
1259
|
+
echo " *** DRY RUN — no files were changed ***"
|
|
1260
|
+
echo " Run without --dry-run to apply changes."
|
|
1261
|
+
fi
|
|
415
1262
|
}
|
|
416
1263
|
|
|
417
1264
|
main() {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
esac
|
|
1265
|
+
parse_args "$@"
|
|
1266
|
+
prompt_start_action
|
|
1267
|
+
case "$ACTION" in
|
|
1268
|
+
uninstall) run_uninstall ;;
|
|
1269
|
+
install|update|reinstall) run_install_like ;;
|
|
1270
|
+
*)
|
|
1271
|
+
usage
|
|
1272
|
+
exit 1
|
|
1273
|
+
;;
|
|
1274
|
+
esac
|
|
429
1275
|
}
|
|
430
1276
|
|
|
431
1277
|
main "$@"
|