orca-agent-cleanup 0.0.0-stage → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,187 @@
1
- # Temporary Holding Version
1
+ <div align="center">
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ![Orca Agent Cleanup](https://raw.githubusercontent.com/wilgon456/orca-agent-cleanup/main/docs/readme-banner.svg)
4
+
5
+ # Orca Agent Cleanup
6
+
7
+ Inspect leftover Orca integrations, quarantine verified residue and restore it when needed.
8
+
9
+ **English** · [한국어](README.ko.md)
10
+
11
+ ![project: utility](https://img.shields.io/badge/project-utility-22d3ee?style=flat-square) ![stack: Node.js 18+](https://img.shields.io/badge/stack-Node.js%2018%2B-22d3ee?style=flat-square) [![License](https://img.shields.io/badge/license-MIT-22d3ee?style=flat-square)](LICENSE)
12
+
13
+ [Quick start](#quick-start) · [Coverage](#cleanup-coverage) · [Restore](#restore)
14
+
15
+ </div>
16
+
17
+ ---
18
+
19
+ > [!IMPORTANT]
20
+ > This is an independent community tool, unaffiliated with Orca or Stably. Uninstall the Orca app normally and close related coding-agent sessions before applying cleanup.
21
+
22
+ ## Why use it
23
+
24
+ Removing Orca can leave shared skills, agent hooks, launchers, cached models and configuration behind. Agents may keep trying to call a missing Orca CLI or computer-use integration. This tool identifies residue using the upstream Orca manifest, ownership markers and signatures, then moves verified items into a recoverable backup.
25
+
26
+ It does **not** uninstall the Orca application itself.
27
+
28
+ ## Quick start
29
+
30
+ Requires **Node.js 18 or newer**. The commands work in Windows PowerShell, macOS Terminal and Linux shells. Run it with `npx`; nothing needs to be installed.
31
+
32
+ ```bash
33
+ npx orca-agent-cleanup scan
34
+ ```
35
+
36
+ You can also clone the repository and replace `npx orca-agent-cleanup` with `node scripts/orca-agent-cleanup.mjs` in the commands below.
37
+
38
+ ```bash
39
+ git clone https://github.com/wilgon456/orca-agent-cleanup.git
40
+ cd orca-agent-cleanup
41
+ ```
42
+
43
+ **Inspect** without changing files:
44
+
45
+ ```bash
46
+ npx orca-agent-cleanup scan
47
+ ```
48
+
49
+ **Preview** cleanup including application data, voice data and leftover Orca processes. If Orca is still installed or running you will see a ⛔ warning; uninstall the app first.
50
+
51
+ ```bash
52
+ npx orca-agent-cleanup clean --dry-run --include-app-data --include-voice-data --stop-processes
53
+ ```
54
+
55
+ **Apply** the reviewed cleanup:
56
+
57
+ ```bash
58
+ npx orca-agent-cleanup clean --include-app-data --include-voice-data --stop-processes
59
+ ```
60
+
61
+ The command reports the backup location. Replace the placeholder below with that actual path:
62
+
63
+ ```bash
64
+ npx orca-agent-cleanup verify --manifest /path/to/backup/manifest.json
65
+ ```
66
+
67
+ Restart your coding agents and terminals afterward so their cached skill lists and environment variables are refreshed.
68
+
69
+ ## Cleanup coverage
70
+
71
+ | Target | Included by default? |
72
+ | --- | --- |
73
+ | Eight official Orca skills and signed copies in Codex plugin caches | Yes, after ownership verification |
74
+ | Orca-managed agent hooks and marked extensions (24 agent integrations, see below) | Yes |
75
+ | Verified user CLI launchers and Orca PATH entries | Yes |
76
+ | Orca background processes left running after the app was removed | `--stop-processes` |
77
+ | Managed hooks and Orca-encrypted credentials under `~/.orca` | Yes |
78
+ | App data and its default voice models | `--include-app-data` |
79
+ | Separate voice caches and custom model locations | `--include-voice-data` |
80
+ | Running WSL distributions | `--include-wsl` |
81
+ | Accessible remote homes | `--include-remote` |
82
+ | All user state under `~/.orca` | `--include-user-state` |
83
+ | Project `.orca` state and adjacent worktree trash | `--include-project-state` |
84
+
85
+ Hook cleanup follows every agent integration in Orca's `src/main/*/hook-service.ts`: Claude, OpenClaude, Codex, Gemini, Antigravity, Cursor, Command Code, Droid, Copilot, Grok, Devin, Kimi, Amp, Hermes, Qoder (including Qoder CN), Qwen Code, CodeBuddy, ZCode, Kiro (each agent file in `~/.kiro/agents`), Muse, jcode, DeepSeek Harness (`~/.dsh/cordis.patch.yml`), OpenCode (status plugins and `tui.json` registrations) and MiMo Code (inside app data). Windows hook commands wrapped in PowerShell or `-EncodedCommand` are decoded before matching. Only Orca entries are removed; the rest of each file is kept.
86
+
87
+ The credential files are `openai-speech-token.enc`, `bitbucket-credential.*`, `linear-token.enc`, `linear-workspaces.json`, `jira-tokens`, `jira-sites.json`, `zcode-plan-api-key.enc`, `opencode-go-api-key.enc` and `minimax-*.enc`. Orca encrypts them with its own app key, so they are useless once the app is gone. Other files under `~/.orca`, such as worktrees and `keybindings.json`, stay unless you pass `--include-user-state`.
88
+
89
+ The official skill names are `computer-use`, `orca-cli`, `orchestration`, `orca-per-workspace-env`, `orca-emulator`, `orca-emulator-android`, `orca-linear` and `linear-tickets`. A matching name alone is insufficient: unrelated providers' skills, including OpenAI's computer-use and Paseo skills, are preserved.
90
+
91
+ ## Platforms
92
+
93
+ | Platform | Relevant locations |
94
+ | --- | --- |
95
+ | Windows 10 / 11 | User skill folders, `%APPDATA%`, `%LOCALAPPDATA%`, verified `ProgramData` / `Public` voice caches and user PATH |
96
+ | macOS | User skill folders, Stably app data under `~/Library`, verified `/usr/local/bin/orca` and `~/.local/bin/orca` launchers |
97
+ | Linux | Orca config/cache directories, AppImage cache, official `orca-ide` launcher and marked Orca dispatcher |
98
+
99
+ Linux's `/usr/bin/orca` is the GNOME screen reader and is never targeted. System-managed or headless-server installations are outside automatic cleanup.
100
+
101
+ ## Safeguards
102
+
103
+ - `scan` is read-only; `clean --dry-run` previews the proposed operations.
104
+ - File names are checked together with provenance and Orca-specific markers.
105
+ - Targets are quarantined rather than permanently deleted.
106
+ - Configuration changes remove identified Orca entries while preserving unrelated settings and saving the original.
107
+ - Manifest v2 is written before cleanup and updated around each operation so interrupted runs can be recovered.
108
+ - File and directory SHA-256 hashes support backup verification.
109
+ - Default backup folders include a UUID; unsafe or nonempty custom destinations are rejected.
110
+ - Items with insufficient provenance are reported as `unverified` and left alone.
111
+
112
+ Default backups live under `~/OrcaAgentCleanupBackups/<timestamp-UUID>/`. They contain `manifest.json`, original configurations and categorized quarantined files.
113
+
114
+ ## Restore
115
+
116
+ Close related applications, verify the backup, then preview restoration:
117
+
118
+ ```bash
119
+ npx orca-agent-cleanup verify --manifest /path/to/backup/manifest.json
120
+ npx orca-agent-cleanup restore --dry-run --manifest /path/to/backup/manifest.json
121
+ npx orca-agent-cleanup restore --manifest /path/to/backup/manifest.json
122
+ ```
123
+
124
+ Manifest v1 backups remain supported, but they lack the content hashes recorded by v2. An `in-progress` v2 journal can recover operations that reached the backup even if the original cleanup was interrupted.
125
+
126
+ If a restore destination has changed, its current contents are preserved in `restore-conflicts/` before the original is restored. Restoration writes a separate report and skips identical items already restored. Keep the backup until you have checked that your agents work correctly.
127
+
128
+ ## Options
129
+
130
+ | Option | Purpose |
131
+ | --- | --- |
132
+ | `scan`, `clean`, `verify`, `restore` | Inspect, quarantine, verify integrity or recover |
133
+ | `--dry-run` | Preview cleanup or restoration |
134
+ | `--stop-processes` | Stop Orca background processes left running without the app; not restorable |
135
+ | `--force` | Clean even while Orca is installed or running; not recommended because the app reinstalls its hooks |
136
+ | `--project <path>` | Add a project's skill folders; repeatable |
137
+ | `--include-app-data`, `--include-voice-data` | Include explicitly selected application / voice residue |
138
+ | `--voice-model-path <path>` | Add a custom voice model path; repeatable |
139
+ | `--include-wsl`, `--wsl-home <path>` | Inspect running WSL distributions or specified WSL homes |
140
+ | `--include-remote`, `--remote-home <path>` | Include an accessible remote home; no automatic SSH login |
141
+ | `--include-user-state` | Quarantine all `~/.orca` state, including sessions and credentials |
142
+ | `--include-project-state` | Include the selected project's `.orca` state and worktree trash |
143
+ | `--backup-root <path>` | Choose a backup destination |
144
+ | `--manifest <path>` | Select a backup for verification or restoration |
145
+ | `--json` | Return structured output |
146
+
147
+ Use `npx orca-agent-cleanup --help` for the complete interface. In PowerShell, use single-line commands or PowerShell backticks rather than Bash line continuations.
148
+
149
+ ## Boundaries
150
+
151
+ Automatic cleanup does not remove the app, change macOS privacy permissions, uninstall Windows app entries, delete package-managed Linux installations, start stopped WSL distributions or log into SSH hosts. It preserves unverified skills, user source/workspaces, historical chats and credentials belonging to other software.
152
+
153
+ The app can be uninstalled through Windows Settings, the macOS Applications folder or the Linux installation method you originally used. Complete that step before cleaning its integrations.
154
+
155
+ ## Version 1.5.0
156
+
157
+ This version runs with `npx orca-agent-cleanup`. `clean` now stops when the Orca app is still installed or running, because Orca reinstalls its hooks and plugins on launch and quarantining its credentials would break a working app; `--force` overrides this. Orca terminal daemons left running after uninstalling are reported by executable location and stopped with `--stop-processes`. Files Orca creates outright, such as Copilot `hooks/orca.json` and Grok `hooks/orca-status.json`, are quarantined whole instead of being left as empty shells when nothing but Orca entries remain.
158
+
159
+ ## Version 1.4.0
160
+
161
+ This version is checked against Orca v1.4.224 and the 2026-10-14 `main` branch. It adds hook cleanup for Qoder, Qwen Code, CodeBuddy, ZCode, Kiro, Muse, jcode, DeepSeek Harness and OpenCode, catches PowerShell-wrapped and base64-encoded Windows hook commands, quarantines Orca's newer encrypted credentials, scans three new skill folders and makes the upstream monitor flag new or removed agent integrations.
162
+
163
+ ## Version 1.3.0
164
+
165
+ Version 1.3.0 added Linux AppImage and signed plugin-cache coverage, recovery journals, backup hashes, UUID destinations, upstream contract monitoring, SHA-pinned Actions and Node.js 24 / CodeQL checks.
166
+
167
+ ## Development
168
+
169
+ Tests use temporary fixture homes rather than real user settings:
170
+
171
+ ```bash
172
+ node --test tests/orca-agent-cleanup.test.mjs
173
+ ```
174
+
175
+ The legacy Windows interface remains available:
176
+
177
+ ```powershell
178
+ .\scripts\Get-OrcaAgentResidue.ps1
179
+ .\scripts\Remove-OrcaAgentIntegrations.ps1 -Apply -IncludeAppData -WhatIf
180
+ .\tests\Invoke-SmokeTest.ps1
181
+ ```
182
+
183
+ See [SECURITY.md](SECURITY.md) for vulnerability reporting. For bug reports, include the OS, whether Orca was uninstalled, a redacted scan result and the expected versus observed behavior.
184
+
185
+ ## License
186
+
187
+ [MIT](LICENSE).
package/package.json CHANGED
@@ -1,6 +1,42 @@
1
- {
2
- "name": "orca-agent-cleanup",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "orca-agent-cleanup",
3
+ "version": "1.5.0",
4
+ "description": "Find and safely quarantine leftover Orca (stablyai/orca) skills, agent hooks, CLI launchers, credentials and app data after uninstalling Orca, with verified restore.",
5
+ "keywords": [
6
+ "orca",
7
+ "stablyai",
8
+ "uninstall",
9
+ "cleanup",
10
+ "claude-code",
11
+ "codex",
12
+ "agent-hooks",
13
+ "skills"
14
+ ],
15
+ "license": "MIT",
16
+ "author": "wilgon456",
17
+ "homepage": "https://github.com/wilgon456/orca-agent-cleanup#readme",
18
+ "repository": {
19
+ "type": "git",
20
+ "url": "git+https://github.com/wilgon456/orca-agent-cleanup.git"
21
+ },
22
+ "bugs": {
23
+ "url": "https://github.com/wilgon456/orca-agent-cleanup/issues"
24
+ },
25
+ "type": "module",
26
+ "bin": {
27
+ "orca-agent-cleanup": "scripts/orca-agent-cleanup.mjs"
28
+ },
29
+ "files": [
30
+ "scripts/orca-agent-cleanup.mjs",
31
+ "scripts/orca-cleanup-core.mjs",
32
+ "README.md",
33
+ "README.ko.md",
34
+ "LICENSE"
35
+ ],
36
+ "engines": {
37
+ "node": ">=18"
38
+ },
39
+ "scripts": {
40
+ "test": "node --test tests/orca-agent-cleanup.test.mjs"
41
+ }
42
+ }
@@ -0,0 +1,221 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from 'node:fs';
4
+ import process from 'node:process';
5
+ import {
6
+ buildContext,
7
+ cleanOrcaResidue,
8
+ OFFICIAL_ORCA_SKILLS,
9
+ restoreOrcaBackup,
10
+ scanOrcaResidue,
11
+ verifyOrcaBackup,
12
+ } from './orca-cleanup-core.mjs';
13
+
14
+ const VERSION = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8')).version;
15
+
16
+ const HELP = `
17
+ Orca Agent Cleanup v${VERSION} : Windows·macOS·Linux 공통 정리 도구
18
+
19
+ 사용법:
20
+ npx orca-agent-cleanup scan [옵션]
21
+ npx orca-agent-cleanup clean [옵션]
22
+ npx orca-agent-cleanup restore --manifest <manifest.json> [옵션]
23
+ npx orca-agent-cleanup verify --manifest <manifest.json> [옵션]
24
+
25
+ 저장소에서 직접 실행할 때는 npx orca-agent-cleanup 대신 node scripts/orca-agent-cleanup.mjs를 씁니다.
26
+
27
+ 명령:
28
+ scan 잔재를 검사합니다(기본 명령, 변경 없음).
29
+ clean 확인된 잔재를 백업 폴더로 격리합니다.
30
+ restore manifest에 기록된 파일과 설정을 복원합니다.
31
+ verify manifest와 백업 내용의 무결성을 검사합니다.
32
+
33
+ 옵션:
34
+ --dry-run clean의 예정 작업만 표시합니다.
35
+ --stop-processes 앱 없이 남아 도는 Orca 백그라운드 프로세스를 끕니다.
36
+ --force Orca 앱이 설치·실행 중이어도 정리합니다(권장하지 않음).
37
+ --include-app-data Orca 앱 데이터·캐시를 포함합니다.
38
+ --include-voice-data 별도 음성 모델 캐시를 포함합니다.
39
+ --include-user-state ~/.orca 전체(자격증명·세션 포함)를 포함합니다.
40
+ --include-wsl 실행 중인 WSL 배포판의 Orca 잔재를 검사·정리합니다.
41
+ --wsl-home <경로> WSL 홈 경로를 직접 추가합니다(반복 가능).
42
+ --include-remote SSH 릴레이·원격 세션 잔재를 포함합니다.
43
+ --remote-home <경로> 접근 가능한 원격 홈 경로를 추가합니다(반복 가능).
44
+ --include-project-state --project의 .orca·worktree 휴지통을 포함합니다.
45
+ --project <경로> 프로젝트 스킬 위치를 추가합니다(반복 가능).
46
+ --voice-model-path <경로> 사용자 지정 음성 모델 경로를 포함합니다(반복 가능).
47
+ --backup-root <경로> 격리·백업 폴더를 지정합니다.
48
+ --manifest <경로> restore·verify에 사용할 manifest.json을 지정합니다.
49
+ --json 결과를 JSON으로 출력합니다.
50
+ -v, --version 버전을 표시합니다.
51
+ -h, --help 도움말을 표시합니다.
52
+
53
+ 예시:
54
+ npx orca-agent-cleanup scan
55
+ npx orca-agent-cleanup clean --dry-run --include-app-data --include-voice-data --stop-processes
56
+ npx orca-agent-cleanup clean --include-app-data --include-voice-data --stop-processes
57
+ npx orca-agent-cleanup restore --dry-run --manifest /path/to/manifest.json
58
+ `;
59
+
60
+ function parseArgs(argv) {
61
+ const result = {
62
+ command: 'scan', projects: [], customVoicePaths: [], wslHomes: [], remoteHomes: [], json: false,
63
+ dryRun: false, force: false, stopProcesses: false, includeAppData: false, includeVoiceData: false,
64
+ includeUserState: false, includeWsl: false, includeRemote: false, includeProjectState: false,
65
+ };
66
+ const args = [...argv];
67
+ if (args[0] && !args[0].startsWith('-')) result.command = args.shift();
68
+ while (args.length) {
69
+ const arg = args.shift();
70
+ if (arg === '--dry-run') result.dryRun = true;
71
+ else if (arg === '--force') result.force = true;
72
+ else if (arg === '--stop-processes') result.stopProcesses = true;
73
+ else if (arg === '--include-app-data') result.includeAppData = true;
74
+ else if (arg === '--include-voice-data') result.includeVoiceData = true;
75
+ else if (arg === '--include-user-state') result.includeUserState = true;
76
+ else if (arg === '--include-wsl') result.includeWsl = true;
77
+ else if (arg === '--include-remote') result.includeRemote = true;
78
+ else if (arg === '--include-project-state') result.includeProjectState = true;
79
+ else if (arg === '--json') result.json = true;
80
+ else if (arg === '-v' || arg === '--version') result.version = true;
81
+ else if (arg === '-h' || arg === '--help') result.help = true;
82
+ else if (arg === '--project') result.projects.push(requireValue(arg, args));
83
+ else if (arg === '--wsl-home') result.wslHomes.push(requireValue(arg, args));
84
+ else if (arg === '--remote-home') result.remoteHomes.push(requireValue(arg, args));
85
+ else if (arg === '--voice-model-path') result.customVoicePaths.push(requireValue(arg, args));
86
+ else if (arg === '--backup-root') result.backupRoot = requireValue(arg, args);
87
+ else if (arg === '--manifest') result.manifestPath = requireValue(arg, args);
88
+ else throw new Error(`알 수 없는 옵션입니다: ${arg}`);
89
+ }
90
+ if (!['scan', 'clean', 'restore', 'verify'].includes(result.command)) throw new Error(`알 수 없는 명령입니다: ${result.command}`);
91
+ if (['restore', 'verify'].includes(result.command) && !result.manifestPath && !result.help && !result.version) {
92
+ throw new Error(`${result.command}에는 --manifest <경로>가 필요합니다.`);
93
+ }
94
+ return result;
95
+ }
96
+
97
+ function requireValue(option, args) {
98
+ const value = args.shift();
99
+ if (!value || value.startsWith('--')) throw new Error(`${option} 뒤에 경로가 필요합니다.`);
100
+ return value;
101
+ }
102
+
103
+ function icon(kind) {
104
+ return ({ skill: '🧩', 'skill-lock': '🔒', hook: '🪝', 'shared-state': '🔗',
105
+ 'skill-transaction': '🧹', extension: '🧱', 'app-data': '🗂️', 'voice-data': '🎙️',
106
+ 'user-state': '🔐', 'wsl-state': '🐧', 'remote-state': '🌐', cli: '⌨️',
107
+ 'project-state': '📁', 'app-installed': '⛔', 'app-running': '⛔', process: '⚙️',
108
+ 'path-entry': '🛣️', unverified: '🛡️' })[kind] || '•';
109
+ }
110
+
111
+ function printScan(findings) {
112
+ console.log('\nOrca Agent Cleanup 검사 결과\n');
113
+ if (!findings.length) {
114
+ console.log('✅ 확인된 Orca 잔재가 없습니다.');
115
+ return;
116
+ }
117
+ for (const item of findings) {
118
+ const option = item.requires ? ` [정리 시 --${item.requires.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)} 필요]` : '';
119
+ console.log(`${icon(item.kind)} ${item.path}`);
120
+ console.log(` ${item.reason}${option}`);
121
+ }
122
+ console.log(`\n총 ${findings.length}개가 확인되었습니다. 검사만 수행했으며 변경 사항은 없습니다.`);
123
+ if (findings.some((item) => ['app-installed', 'app-running'].includes(item.kind))) {
124
+ console.log('\n⛔ Orca 앱이 아직 설치돼 있거나 실행 중입니다. 앱이 켜질 때 훅을 다시 설치하므로 먼저 앱을 제거하세요. 그 전까지 clean은 멈춥니다.');
125
+ }
126
+ }
127
+
128
+ function printClean(result) {
129
+ console.log(`\nOrca Agent Cleanup ${result.dryRun ? '미리보기' : '정리 결과'}\n`);
130
+ if (result.blocked && !result.dryRun) {
131
+ console.log(`⛔ ${result.errors[0].message}`);
132
+ return;
133
+ }
134
+ if (result.blocked) {
135
+ console.log('⛔ Orca 앱이 아직 설치돼 있거나 실행 중입니다. 실제 clean은 앱을 제거하기 전까지 멈춥니다.\n');
136
+ }
137
+ if (!result.actions.length) {
138
+ console.log('✅ 정리할 항목이 없습니다.');
139
+ return;
140
+ }
141
+ const labels = {
142
+ quarantined: '✅ 격리', edited: '✅ 수정', 'would-quarantine': '🔎 격리 예정',
143
+ 'would-edit': '🔎 수정 예정', skipped: '⏭️ 옵션 제외', covered: '↳ 상위 상태에 포함',
144
+ unchanged: '➖ 변경 없음', error: '❌ 오류',
145
+ 'pending-quarantine': '⚠️ 격리 상태 확인 필요', 'pending-edit': '⚠️ 수정 상태 확인 필요',
146
+ stopped: '✅ 프로세스 끔', 'would-stop': '🔎 프로세스 끌 예정',
147
+ };
148
+ for (const item of result.actions) console.log(`${labels[item.action] || item.action} ${item.path}`);
149
+ if (!result.dryRun && result.actions.some((item) => ['quarantined', 'edited'].includes(item.action))) {
150
+ console.log(`\n복구용 백업: ${result.backupRoot}`);
151
+ }
152
+ if (result.errors.length) console.log(`\n⚠️ ${result.errors.length}개 항목은 자동 처리하지 못했습니다. 위 오류를 확인하세요.`);
153
+ }
154
+
155
+ function printRestore(result) {
156
+ console.log(`\nOrca Agent Cleanup ${result.dryRun ? '복원 미리보기' : '복원 결과'}\n`);
157
+ if (result.cleanupStatus === 'in-progress') {
158
+ console.log('⚠️ 중단된 정리 journal을 복구합니다. 해시 기록 전 중단된 항목은 백업 존재 여부로 판정합니다.\n');
159
+ }
160
+ if (!result.actions.length) {
161
+ console.log('✅ manifest에 복원할 완료 작업이 없습니다.');
162
+ return;
163
+ }
164
+ const labels = {
165
+ restored: '✅ 복원', 'would-restore': '🔎 복원 예정',
166
+ 'already-restored': '➖ 이미 복원됨', error: '❌ 오류',
167
+ };
168
+ for (const item of result.actions) {
169
+ console.log(`${labels[item.action] || item.action} ${item.path}`);
170
+ if (item.conflictBackup) console.log(` 현재 항목 보존: ${item.conflictBackup}`);
171
+ }
172
+ if (result.reportPath) console.log(`\n복원 보고서: ${result.reportPath}`);
173
+ if (result.errors.length) console.log(`\n⚠️ ${result.errors.length}개 항목은 복원하지 못했습니다.`);
174
+ }
175
+
176
+ function printVerify(result) {
177
+ console.log('\nOrca Agent Cleanup 백업 검증 결과\n');
178
+ const labels = {
179
+ verified: '✅ 검증됨', 'not-recorded': 'ℹ️ v1 해시 없음',
180
+ 'not-applied': '➖ 미적용', 'recoverable-unverified': '⚠️ 중단 작업', error: '❌ 오류',
181
+ };
182
+ for (const item of result.actions) console.log(`${labels[item.status] || item.status} ${item.backup || item.path}`);
183
+ console.log(result.valid ? '\n✅ manifest와 백업을 사용할 수 있습니다.' : `\n❌ ${result.errors.length}개 오류가 있습니다.`);
184
+ }
185
+
186
+ try {
187
+ const options = parseArgs(process.argv.slice(2));
188
+ if (options.version) {
189
+ console.log(VERSION);
190
+ process.exit(0);
191
+ } else if (options.help) {
192
+ console.log(HELP.trim());
193
+ process.exit(0);
194
+ }
195
+ if (options.command === 'verify') {
196
+ const result = verifyOrcaBackup(options.manifestPath, options);
197
+ if (options.json) console.log(JSON.stringify(result, null, 2));
198
+ else printVerify(result);
199
+ if (!result.valid) process.exitCode = 2;
200
+ } else if (options.command === 'restore') {
201
+ const result = restoreOrcaBackup(options.manifestPath, options);
202
+ if (options.json) console.log(JSON.stringify(result, null, 2));
203
+ else printRestore(result);
204
+ if (result.errors.length) process.exitCode = 2;
205
+ } else if (options.command === 'scan') {
206
+ const context = buildContext(options);
207
+ const findings = scanOrcaResidue(context);
208
+ if (options.json) console.log(JSON.stringify({ platform: context.platform, officialSkills: OFFICIAL_ORCA_SKILLS, findings }, null, 2));
209
+ else printScan(findings);
210
+ } else {
211
+ const context = buildContext(options);
212
+ const result = cleanOrcaResidue(context, options);
213
+ if (options.json) console.log(JSON.stringify(result, null, 2));
214
+ else printClean(result);
215
+ if (result.errors.length) process.exitCode = 2;
216
+ }
217
+ } catch (error) {
218
+ console.error(`오류: ${error.message}`);
219
+ console.error('도움말: npx orca-agent-cleanup --help');
220
+ process.exitCode = 1;
221
+ }