oh-my-customcode 1.1.92 → 1.1.94
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 +1 -1
- package/dist/cli/index.js +428 -58
- package/dist/index.js +423 -53
- package/package.json +1 -1
- package/templates/.claude/hooks/scripts/adaptive-harness-scan.sh +2 -1
- package/templates/.claude/hooks/scripts/claude-md-reinject.sh +2 -1
- package/templates/.claude/hooks/scripts/omcustom-auto-update.sh +7 -0
- package/templates/.claude/hooks/scripts/session-autofix.sh +7 -0
- package/templates/.claude/hooks/scripts/session-env-check.sh +7 -0
- package/templates/.claude/hooks/scripts/stale-todo-scanner.sh +7 -0
- package/templates/.claude/rules/MAY-optimization.md +2 -0
- package/templates/.claude/rules/SHOULD-hud-statusline.md +2 -2
- package/templates/.claude/skills/pipeline/workflows/auto-dev.yaml +56 -10
- package/templates/guides/claude-code/15-version-compatibility.md +149 -0
- package/templates/manifest.json +1 -1
- package/templates/workflows/auto-dev.yaml +56 -10
package/dist/cli/index.js
CHANGED
|
@@ -217,7 +217,7 @@ var init_package = __esm(() => {
|
|
|
217
217
|
workspaces: [
|
|
218
218
|
"packages/*"
|
|
219
219
|
],
|
|
220
|
-
version: "1.1.
|
|
220
|
+
version: "1.1.94",
|
|
221
221
|
description: "Batteries-included agent harness for Claude Code",
|
|
222
222
|
type: "module",
|
|
223
223
|
bin: {
|
|
@@ -7353,6 +7353,19 @@ async function writeJsonFile(path, data) {
|
|
|
7353
7353
|
const content = JSON.stringify(data, null, 2);
|
|
7354
7354
|
await fs.writeFile(path, content, "utf-8");
|
|
7355
7355
|
}
|
|
7356
|
+
function parseJsonText(raw) {
|
|
7357
|
+
const bom = raw.startsWith(UTF8_BOM);
|
|
7358
|
+
const text = bom ? raw.slice(UTF8_BOM.length) : raw;
|
|
7359
|
+
return {
|
|
7360
|
+
data: JSON.parse(text),
|
|
7361
|
+
format: { bom, trailingNewline: text.endsWith(`
|
|
7362
|
+
`) }
|
|
7363
|
+
};
|
|
7364
|
+
}
|
|
7365
|
+
function stringifyJson(data, format) {
|
|
7366
|
+
return `${format.bom ? UTF8_BOM : ""}${JSON.stringify(data, null, 2)}${format.trailingNewline ? `
|
|
7367
|
+
` : ""}`;
|
|
7368
|
+
}
|
|
7356
7369
|
async function readTextFile(path) {
|
|
7357
7370
|
const fs = await import("node:fs/promises");
|
|
7358
7371
|
return fs.readFile(path, "utf-8");
|
|
@@ -7408,6 +7421,7 @@ function matchesPattern(filename, pattern) {
|
|
|
7408
7421
|
const regex = new RegExp(`^${regexPattern}$`);
|
|
7409
7422
|
return regex.test(filename);
|
|
7410
7423
|
}
|
|
7424
|
+
var UTF8_BOM = "\uFEFF";
|
|
7411
7425
|
var init_fs = () => {};
|
|
7412
7426
|
|
|
7413
7427
|
// src/cli/projects.ts
|
|
@@ -8220,7 +8234,7 @@ var init_theme = __esm(() => {
|
|
|
8220
8234
|
});
|
|
8221
8235
|
|
|
8222
8236
|
// node_modules/.bun/@inquirer+core@11.1.7+b219b5910764fa5c/node_modules/@inquirer/core/dist/lib/make-theme.js
|
|
8223
|
-
function
|
|
8237
|
+
function isPlainObject2(value) {
|
|
8224
8238
|
if (typeof value !== "object" || value === null)
|
|
8225
8239
|
return false;
|
|
8226
8240
|
let proto = value;
|
|
@@ -8234,7 +8248,7 @@ function deepMerge2(...objects) {
|
|
|
8234
8248
|
for (const obj of objects) {
|
|
8235
8249
|
for (const [key, value] of Object.entries(obj)) {
|
|
8236
8250
|
const prevValue = output[key];
|
|
8237
|
-
output[key] =
|
|
8251
|
+
output[key] = isPlainObject2(prevValue) && isPlainObject2(value) ? deepMerge2(prevValue, value) : value;
|
|
8238
8252
|
}
|
|
8239
8253
|
}
|
|
8240
8254
|
return output;
|
|
@@ -15616,7 +15630,7 @@ var MESSAGES = {
|
|
|
15616
15630
|
"install.entry_md_skipped": "{{entry}} skipped ({{reason}})",
|
|
15617
15631
|
"install.lockfile_generated": "Lockfile generated ({{files}} files tracked)",
|
|
15618
15632
|
"install.lockfile_failed": "Failed to generate lockfile: {{error}}",
|
|
15619
|
-
"install.hook_commands_migrated": "Migrated {{rewritten}} hook command(s) in settings.local.json",
|
|
15633
|
+
"install.hook_commands_migrated": "Migrated {{rewritten}} hook/statusLine command(s) in settings.local.json",
|
|
15620
15634
|
"install.hook_commands_migration_failed": "Failed to migrate hook commands in settings.local.json: {{error}}",
|
|
15621
15635
|
"lockfile.not_found": "Lockfile not found: {{path}}",
|
|
15622
15636
|
"lockfile.invalid_version": "Invalid lockfile version: {{path}}",
|
|
@@ -15639,15 +15653,86 @@ var MESSAGES = {
|
|
|
15639
15653
|
"update.lockfile_failed": "Failed to regenerate lockfile: {{error}}",
|
|
15640
15654
|
"update.protected_file_updated": "⟳ Protected file {{file}} in {{component}} updated: {{hint}}",
|
|
15641
15655
|
"update.namespace_synced": "Namespace synced: {{file}} ({{component}})",
|
|
15642
|
-
"update.hook_commands_migrated": "Migrated {{count}} hook command(s) to CLAUDE_PROJECT_DIR-anchored paths",
|
|
15656
|
+
"update.hook_commands_migrated": "Migrated {{count}} hook/statusLine command(s) to CLAUDE_PROJECT_DIR-anchored paths",
|
|
15643
15657
|
"update.hook_commands_migration_failed": "Failed to migrate hook commands in {{path}}",
|
|
15644
15658
|
"config.load_failed": "Failed to load config: {{error}}",
|
|
15645
15659
|
"config.not_found": "Config not found at {{path}}, using defaults",
|
|
15646
15660
|
"config.saved": "Config saved to {{path}}",
|
|
15647
15661
|
"config.deleted": "Config deleted from {{path}}",
|
|
15662
|
+
"config.invalid_preserve_path": "Ignored invalid preserve path: {{path}} ({{reason}})",
|
|
15663
|
+
"codex.already_installed": "Codex CLI is already installed",
|
|
15664
|
+
"codex.installing_brew": "Installing Codex CLI via Homebrew...",
|
|
15665
|
+
"codex.installing_npm": "Installing Codex CLI via npm...",
|
|
15666
|
+
"codex.install_failed": "Failed to install Codex CLI: {{error}}",
|
|
15667
|
+
"codex.unsupported_os": "Codex CLI auto-install is not supported on {{os}}",
|
|
15668
|
+
"rtk.already_installed": "RTK is already installed",
|
|
15669
|
+
"rtk.installing_brew": "Installing RTK via Homebrew...",
|
|
15670
|
+
"rtk.installing_curl": "Installing RTK via install script...",
|
|
15671
|
+
"rtk.install_failed": "Failed to install RTK: {{error}}",
|
|
15672
|
+
"rtk.unsupported_os": "RTK auto-install is not supported on {{os}}",
|
|
15673
|
+
"mcp.ontology_rag_configured": "ontology-rag MCP server configured successfully",
|
|
15674
|
+
"install.codex_installing": "Installing Codex CLI...",
|
|
15675
|
+
"install.codex_success": "Codex CLI installed successfully",
|
|
15676
|
+
"install.codex_already": "Codex CLI is already installed",
|
|
15677
|
+
"install.rtk_installing": "Installing RTK...",
|
|
15678
|
+
"install.rtk_success": "RTK installed successfully",
|
|
15679
|
+
"install.rtk_already": "RTK is already installed",
|
|
15680
|
+
"install.preserved": "Preserved {{files}} file(s) and {{dirs}} directory(ies) from the existing installation",
|
|
15681
|
+
"install.restored": "Restored {{files}} file(s) and {{dirs}} directory(ies) after installation",
|
|
15682
|
+
"install.backup_failed": "Failed to back up {{path}}: {{error}}",
|
|
15683
|
+
"install.hooks_settings_failed": "Failed to merge hooks into settings.local.json: {{error}}",
|
|
15684
|
+
"preserve.cleanup_failed": "Failed to clean up preservation directory {{dir}}: {{error}}",
|
|
15685
|
+
"preserve.extract_dir_failed": "Failed to preserve directory {{dir}}: {{error}}",
|
|
15686
|
+
"preserve.extract_failed": "Failed to preserve file {{file}}: {{error}}",
|
|
15687
|
+
"preserve.restore_dir_failed": "Failed to restore directory {{dir}}: {{error}}",
|
|
15688
|
+
"preserve.restore_failed": "Failed to restore file {{file}}: {{error}}",
|
|
15689
|
+
"preserve_files.invalid_path": "Ignored invalid preserve_files entry from {{source}}: {{path}} ({{reason}})",
|
|
15690
|
+
"update.codex_missing": "Codex CLI is not installed",
|
|
15691
|
+
"update.rtk_missing": "RTK is not installed",
|
|
15692
|
+
"update.self_update_skipped": "Skipped update: source project cannot update itself",
|
|
15693
|
+
"update.deprecated_file_invalid_path": "Skipped deprecated file with invalid path: {{path}} ({{reason}})",
|
|
15694
|
+
"update.deprecated_file_removed": "Removed deprecated file: {{path}} ({{reason}})",
|
|
15695
|
+
"update.entry_doc_created": "Entry document created: {{path}}",
|
|
15696
|
+
"update.entry_doc_force_updated": "Entry document force-updated: {{path}}",
|
|
15697
|
+
"update.entry_merge_warning": "Entry document merge warning: {{warning}}",
|
|
15698
|
+
"update.entry_template_not_found": "Entry document template not found: {{template}}",
|
|
15699
|
+
"update.protected_file_force_overwrite": "Protected file {{file}} in {{component}} overwritten: {{hint}}",
|
|
15700
|
+
"update.protected_file_skipped": "Protected file {{file}} in {{component}} skipped: {{hint}}",
|
|
15701
|
+
"update.settings_local_backfill_failed": "Failed to backfill statusLine refreshInterval in {{path}}",
|
|
15648
15702
|
"general.done": "Done!",
|
|
15649
15703
|
"general.failed": "Failed",
|
|
15650
|
-
"general.skipped": "Skipped"
|
|
15704
|
+
"general.skipped": "Skipped",
|
|
15705
|
+
"install.agent_domain_excluded": "Excluded agent {{agent}} (domain: {{domain}})",
|
|
15706
|
+
"install.backed_up": "Backed up {{from}} to {{to}}",
|
|
15707
|
+
"install.hooks_settings_merged": "Merged hooks wiring into settings.local.json",
|
|
15708
|
+
"install.hooks_settings_skipped": "Skipped hooks wiring in settings.local.json: {{reason}}",
|
|
15709
|
+
"install.schemas_not_found": "schemas/tool-inputs.json template not found: {{path}}",
|
|
15710
|
+
"install.schemas_skipped": "Skipped schemas/tool-inputs.json: {{reason}}",
|
|
15711
|
+
"install.schemas_installed": "Installed schemas/tool-inputs.json",
|
|
15712
|
+
"install.settings_local_created": "Created settings.local.json",
|
|
15713
|
+
"install.settings_local_merged": "Merged statusLine into settings.local.json",
|
|
15714
|
+
"install.settings_local_refreshInterval_added": "Added statusLine refreshInterval to settings.local.json",
|
|
15715
|
+
"install.settings_local_skipped": "Skipped settings.local.json statusLine: {{reason}}",
|
|
15716
|
+
"install.settings_local_statusline_migrated": "Re-anchored default statusLine command in settings.local.json",
|
|
15717
|
+
"install.skill_scope_excluded": "Excluded skill {{skill}} (scope: {{scope}})",
|
|
15718
|
+
"install.statusline_installed": "Installed statusline.sh",
|
|
15719
|
+
"install.statusline_not_found": "statusline.sh template not found: {{path}}",
|
|
15720
|
+
"install.statusline_skipped": "Skipped statusline.sh: {{reason}}",
|
|
15721
|
+
"install.tests_config_installed": "Installed tests/tsconfig.json",
|
|
15722
|
+
"install.tests_config_not_found": "tests/tsconfig.json template not found: {{path}}",
|
|
15723
|
+
"install.tests_config_skipped": "Skipped tests/tsconfig.json: {{reason}}",
|
|
15724
|
+
"preserve.cleanup": "Cleaned up preservation directory: {{dir}}",
|
|
15725
|
+
"preserve.copied_json": "Copied JSON file: {{file}}",
|
|
15726
|
+
"preserve.extracted_dir": "Preserved directory: {{dir}}",
|
|
15727
|
+
"preserve.extracted_file": "Preserved file: {{file}}",
|
|
15728
|
+
"preserve.merged_json": "Merged JSON file: {{file}}",
|
|
15729
|
+
"preserve.restored_dir": "Restored directory: {{dir}}",
|
|
15730
|
+
"preserve.restored_file": "Restored file: {{file}}",
|
|
15731
|
+
"update.deprecated_files_cleaned": "Removed {{count}} deprecated file(s)",
|
|
15732
|
+
"update.entry_doc_merged": "Entry document merged: {{path}} ({{managed}} managed, {{custom}} custom sections)",
|
|
15733
|
+
"update.file_backed_up": "Backed up {{path}} to {{backup}}",
|
|
15734
|
+
"update.root_files_synced": "Root-level files synced: {{files}}",
|
|
15735
|
+
"update.settings_local_refreshInterval_backfilled": "Backfilled statusLine refreshInterval in settings.local.json"
|
|
15651
15736
|
},
|
|
15652
15737
|
ko: {
|
|
15653
15738
|
"install.start": "oh-my-customcode 초기화 중...",
|
|
@@ -15666,7 +15751,7 @@ var MESSAGES = {
|
|
|
15666
15751
|
"install.entry_md_skipped": "{{entry}} 건너뜀 ({{reason}})",
|
|
15667
15752
|
"install.lockfile_generated": "잠금 파일 생성 완료 ({{files}}개 파일 추적)",
|
|
15668
15753
|
"install.lockfile_failed": "잠금 파일 생성 실패: {{error}}",
|
|
15669
|
-
"install.hook_commands_migrated": "settings.local.json의
|
|
15754
|
+
"install.hook_commands_migrated": "settings.local.json의 훅/상태줄 명령 {{rewritten}}개를 마이그레이션했습니다",
|
|
15670
15755
|
"install.hook_commands_migration_failed": "settings.local.json의 훅 명령 마이그레이션에 실패했습니다: {{error}}",
|
|
15671
15756
|
"lockfile.not_found": "잠금 파일 없음: {{path}}",
|
|
15672
15757
|
"lockfile.invalid_version": "잠금 파일 버전 유효하지 않음: {{path}}",
|
|
@@ -15689,15 +15774,86 @@ var MESSAGES = {
|
|
|
15689
15774
|
"update.lockfile_failed": "잠금 파일 재생성 실패: {{error}}",
|
|
15690
15775
|
"update.protected_file_updated": "⟳ 보호 파일 {{file}} ({{component}}) 업데이트됨: {{hint}}",
|
|
15691
15776
|
"update.namespace_synced": "네임스페이스 동기화: {{file}} ({{component}})",
|
|
15692
|
-
"update.hook_commands_migrated": "CLAUDE_PROJECT_DIR 기준 경로로
|
|
15777
|
+
"update.hook_commands_migrated": "CLAUDE_PROJECT_DIR 기준 경로로 훅/상태줄 명령 {{count}}개를 마이그레이션했습니다",
|
|
15693
15778
|
"update.hook_commands_migration_failed": "{{path}}의 훅 명령 마이그레이션에 실패했습니다",
|
|
15694
15779
|
"config.load_failed": "설정 로드 실패: {{error}}",
|
|
15695
15780
|
"config.not_found": "{{path}}에 설정 없음, 기본값 사용",
|
|
15696
15781
|
"config.saved": "설정 저장: {{path}}",
|
|
15697
15782
|
"config.deleted": "설정 삭제: {{path}}",
|
|
15783
|
+
"config.invalid_preserve_path": "유효하지 않은 보존 경로를 무시했습니다: {{path}} ({{reason}})",
|
|
15784
|
+
"codex.already_installed": "Codex CLI가 이미 설치되어 있습니다",
|
|
15785
|
+
"codex.installing_brew": "Homebrew로 Codex CLI를 설치합니다...",
|
|
15786
|
+
"codex.installing_npm": "npm으로 Codex CLI를 설치합니다...",
|
|
15787
|
+
"codex.install_failed": "Codex CLI 설치에 실패했습니다: {{error}}",
|
|
15788
|
+
"codex.unsupported_os": "{{os}}에서는 Codex CLI 자동 설치를 지원하지 않습니다",
|
|
15789
|
+
"rtk.already_installed": "RTK가 이미 설치되어 있습니다",
|
|
15790
|
+
"rtk.installing_brew": "Homebrew로 RTK를 설치합니다...",
|
|
15791
|
+
"rtk.installing_curl": "설치 스크립트로 RTK를 설치합니다...",
|
|
15792
|
+
"rtk.install_failed": "RTK 설치에 실패했습니다: {{error}}",
|
|
15793
|
+
"rtk.unsupported_os": "{{os}}에서는 RTK 자동 설치를 지원하지 않습니다",
|
|
15794
|
+
"mcp.ontology_rag_configured": "ontology-rag MCP 서버 구성을 완료했습니다",
|
|
15795
|
+
"install.codex_installing": "Codex CLI를 설치합니다...",
|
|
15796
|
+
"install.codex_success": "Codex CLI를 설치했습니다",
|
|
15797
|
+
"install.codex_already": "Codex CLI가 이미 설치되어 있습니다",
|
|
15798
|
+
"install.rtk_installing": "RTK를 설치합니다...",
|
|
15799
|
+
"install.rtk_success": "RTK를 설치했습니다",
|
|
15800
|
+
"install.rtk_already": "RTK가 이미 설치되어 있습니다",
|
|
15801
|
+
"install.preserved": "기존 설치에서 파일 {{files}}개와 디렉토리 {{dirs}}개를 보존했습니다",
|
|
15802
|
+
"install.restored": "설치 후 파일 {{files}}개와 디렉토리 {{dirs}}개를 복원했습니다",
|
|
15803
|
+
"install.backup_failed": "{{path}} 백업에 실패했습니다: {{error}}",
|
|
15804
|
+
"install.hooks_settings_failed": "settings.local.json에 훅을 병합하지 못했습니다: {{error}}",
|
|
15805
|
+
"preserve.cleanup_failed": "보존 디렉토리 {{dir}} 정리에 실패했습니다: {{error}}",
|
|
15806
|
+
"preserve.extract_dir_failed": "디렉토리 {{dir}} 보존에 실패했습니다: {{error}}",
|
|
15807
|
+
"preserve.extract_failed": "파일 {{file}} 보존에 실패했습니다: {{error}}",
|
|
15808
|
+
"preserve.restore_dir_failed": "디렉토리 {{dir}} 복원에 실패했습니다: {{error}}",
|
|
15809
|
+
"preserve.restore_failed": "파일 {{file}} 복원에 실패했습니다: {{error}}",
|
|
15810
|
+
"preserve_files.invalid_path": "{{source}}의 유효하지 않은 preserve_files 항목을 무시했습니다: {{path}} ({{reason}})",
|
|
15811
|
+
"update.codex_missing": "Codex CLI가 설치되어 있지 않습니다",
|
|
15812
|
+
"update.rtk_missing": "RTK가 설치되어 있지 않습니다",
|
|
15813
|
+
"update.self_update_skipped": "업데이트를 건너뜁니다: 소스 프로젝트는 스스로 업데이트할 수 없습니다",
|
|
15814
|
+
"update.deprecated_file_invalid_path": "경로가 유효하지 않아 지원 중단 파일을 건너뜁니다: {{path}} ({{reason}})",
|
|
15815
|
+
"update.deprecated_file_removed": "지원 중단 파일을 삭제했습니다: {{path}} ({{reason}})",
|
|
15816
|
+
"update.entry_doc_created": "진입 문서를 생성했습니다: {{path}}",
|
|
15817
|
+
"update.entry_doc_force_updated": "진입 문서를 강제로 업데이트했습니다: {{path}}",
|
|
15818
|
+
"update.entry_merge_warning": "진입 문서 병합 경고: {{warning}}",
|
|
15819
|
+
"update.entry_template_not_found": "진입 문서 템플릿을 찾을 수 없습니다: {{template}}",
|
|
15820
|
+
"update.protected_file_force_overwrite": "{{component}}의 보호 파일 {{file}}을(를) 덮어썼습니다: {{hint}}",
|
|
15821
|
+
"update.protected_file_skipped": "{{component}}의 보호 파일 {{file}}을(를) 건너뜁니다: {{hint}}",
|
|
15822
|
+
"update.settings_local_backfill_failed": "{{path}}의 statusLine refreshInterval 보충에 실패했습니다",
|
|
15698
15823
|
"general.done": "완료!",
|
|
15699
15824
|
"general.failed": "실패",
|
|
15700
|
-
"general.skipped": "건너뜀"
|
|
15825
|
+
"general.skipped": "건너뜀",
|
|
15826
|
+
"install.agent_domain_excluded": "에이전트 {{agent}}을(를) 제외했습니다 (도메인: {{domain}})",
|
|
15827
|
+
"install.backed_up": "{{from}}을(를) {{to}}(으)로 백업했습니다",
|
|
15828
|
+
"install.hooks_settings_merged": "settings.local.json에 훅 연결을 병합했습니다",
|
|
15829
|
+
"install.hooks_settings_skipped": "settings.local.json의 훅 연결을 건너뜁니다: {{reason}}",
|
|
15830
|
+
"install.schemas_not_found": "schemas/tool-inputs.json 템플릿을 찾을 수 없습니다: {{path}}",
|
|
15831
|
+
"install.schemas_skipped": "schemas/tool-inputs.json 설치를 건너뜁니다: {{reason}}",
|
|
15832
|
+
"install.schemas_installed": "schemas/tool-inputs.json을 설치했습니다",
|
|
15833
|
+
"install.settings_local_created": "settings.local.json을 생성했습니다",
|
|
15834
|
+
"install.settings_local_merged": "settings.local.json에 statusLine을 병합했습니다",
|
|
15835
|
+
"install.settings_local_refreshInterval_added": "settings.local.json에 statusLine refreshInterval을 추가했습니다",
|
|
15836
|
+
"install.settings_local_skipped": "settings.local.json의 statusLine을 건너뜁니다: {{reason}}",
|
|
15837
|
+
"install.settings_local_statusline_migrated": "settings.local.json의 기본 statusLine 명령을 프로젝트 루트 기준으로 변경했습니다",
|
|
15838
|
+
"install.skill_scope_excluded": "스킬 {{skill}}을(를) 제외했습니다 (범위: {{scope}})",
|
|
15839
|
+
"install.statusline_installed": "statusline.sh를 설치했습니다",
|
|
15840
|
+
"install.statusline_not_found": "statusline.sh 템플릿을 찾을 수 없습니다: {{path}}",
|
|
15841
|
+
"install.statusline_skipped": "statusline.sh 설치를 건너뜁니다: {{reason}}",
|
|
15842
|
+
"install.tests_config_installed": "tests/tsconfig.json을 설치했습니다",
|
|
15843
|
+
"install.tests_config_not_found": "tests/tsconfig.json 템플릿을 찾을 수 없습니다: {{path}}",
|
|
15844
|
+
"install.tests_config_skipped": "tests/tsconfig.json 설치를 건너뜁니다: {{reason}}",
|
|
15845
|
+
"preserve.cleanup": "보존 디렉토리를 정리했습니다: {{dir}}",
|
|
15846
|
+
"preserve.copied_json": "JSON 파일을 복사했습니다: {{file}}",
|
|
15847
|
+
"preserve.extracted_dir": "디렉토리를 보존했습니다: {{dir}}",
|
|
15848
|
+
"preserve.extracted_file": "파일을 보존했습니다: {{file}}",
|
|
15849
|
+
"preserve.merged_json": "JSON 파일을 병합했습니다: {{file}}",
|
|
15850
|
+
"preserve.restored_dir": "디렉토리를 복원했습니다: {{dir}}",
|
|
15851
|
+
"preserve.restored_file": "파일을 복원했습니다: {{file}}",
|
|
15852
|
+
"update.deprecated_files_cleaned": "지원 중단 파일 {{count}}개를 삭제했습니다",
|
|
15853
|
+
"update.entry_doc_merged": "진입 문서를 병합했습니다: {{path}} (관리 섹션 {{managed}}개, 사용자 섹션 {{custom}}개)",
|
|
15854
|
+
"update.file_backed_up": "{{path}}을(를) {{backup}}(으)로 백업했습니다",
|
|
15855
|
+
"update.root_files_synced": "루트 수준 파일을 동기화했습니다: {{files}}",
|
|
15856
|
+
"update.settings_local_refreshInterval_backfilled": "settings.local.json의 statusLine refreshInterval을 보충했습니다"
|
|
15701
15857
|
}
|
|
15702
15858
|
};
|
|
15703
15859
|
function getMessage(key, params) {
|
|
@@ -16961,11 +17117,122 @@ import {
|
|
|
16961
17117
|
rename,
|
|
16962
17118
|
stat as stat2
|
|
16963
17119
|
} from "node:fs/promises";
|
|
16964
|
-
import { basename as basename3, join as join8 } from "node:path";
|
|
17120
|
+
import { basename as basename3, dirname as dirname4, join as join8 } from "node:path";
|
|
16965
17121
|
|
|
16966
17122
|
// src/core/file-preservation.ts
|
|
16967
17123
|
init_fs();
|
|
16968
17124
|
import { basename as basename2, join as join7 } from "node:path";
|
|
17125
|
+
|
|
17126
|
+
// src/core/hook-group-merge.ts
|
|
17127
|
+
var LEGACY_OMCUSTOM_DESCRIPTIONS = [
|
|
17128
|
+
"Record agent outcomes + auto-continue workflow + R007/R008 drift advisory on subagent completion (autonomous-loop re-entry, #1545)",
|
|
17129
|
+
'Re-inject project CLAUDE.md into model context on session start/resume/clear and compact re-entry — matcher "*" covers all SessionStart sources including "compact" (#1617)'
|
|
17130
|
+
];
|
|
17131
|
+
var STANDALONE_SCRIPT_COMMAND = /^(?:bash )?(?:"(?:\$\{CLAUDE_PROJECT_DIR:-\.\}\/|\.\/)?\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh)"|(?:\.\/)?\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh))$/;
|
|
17132
|
+
var SCRIPT_REFERENCE = /\.claude\/hooks\/((?:scripts\/)?[A-Za-z0-9._-]+\.sh)/g;
|
|
17133
|
+
function matcherKey(group) {
|
|
17134
|
+
return typeof group.matcher === "string" ? group.matcher : "";
|
|
17135
|
+
}
|
|
17136
|
+
function isRecord(value) {
|
|
17137
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17138
|
+
}
|
|
17139
|
+
function addHookOwnership(hook, owned) {
|
|
17140
|
+
if (!isRecord(hook) || typeof hook.command !== "string") {
|
|
17141
|
+
return;
|
|
17142
|
+
}
|
|
17143
|
+
owned.commands.add(hook.command);
|
|
17144
|
+
for (const match of hook.command.matchAll(SCRIPT_REFERENCE)) {
|
|
17145
|
+
owned.scripts.add(match[1]);
|
|
17146
|
+
}
|
|
17147
|
+
}
|
|
17148
|
+
function addGroupOwnership(group, owned, descriptions) {
|
|
17149
|
+
if (!isRecord(group)) {
|
|
17150
|
+
return;
|
|
17151
|
+
}
|
|
17152
|
+
const key = matcherKey(group);
|
|
17153
|
+
let forMatcher = owned.get(key);
|
|
17154
|
+
if (forMatcher === undefined) {
|
|
17155
|
+
forMatcher = { commands: new Set, scripts: new Set };
|
|
17156
|
+
owned.set(key, forMatcher);
|
|
17157
|
+
}
|
|
17158
|
+
if (typeof group.description === "string") {
|
|
17159
|
+
descriptions.add(group.description);
|
|
17160
|
+
}
|
|
17161
|
+
if (Array.isArray(group.hooks)) {
|
|
17162
|
+
for (const hook of group.hooks) {
|
|
17163
|
+
addHookOwnership(hook, forMatcher);
|
|
17164
|
+
}
|
|
17165
|
+
}
|
|
17166
|
+
}
|
|
17167
|
+
function collectOwnership(generated) {
|
|
17168
|
+
const descriptions = new Set(LEGACY_OMCUSTOM_DESCRIPTIONS);
|
|
17169
|
+
const events = new Map;
|
|
17170
|
+
for (const [event, groups] of Object.entries(generated)) {
|
|
17171
|
+
const owned = new Map;
|
|
17172
|
+
events.set(event, owned);
|
|
17173
|
+
for (const group of groups) {
|
|
17174
|
+
addGroupOwnership(group, owned, descriptions);
|
|
17175
|
+
}
|
|
17176
|
+
}
|
|
17177
|
+
return { descriptions, events };
|
|
17178
|
+
}
|
|
17179
|
+
function isOwnedCommand(hook, owned) {
|
|
17180
|
+
if (owned === undefined || !isRecord(hook) || hook.type !== "command") {
|
|
17181
|
+
return false;
|
|
17182
|
+
}
|
|
17183
|
+
const command = hook.command;
|
|
17184
|
+
if (typeof command !== "string") {
|
|
17185
|
+
return false;
|
|
17186
|
+
}
|
|
17187
|
+
if (owned.commands.has(command)) {
|
|
17188
|
+
return true;
|
|
17189
|
+
}
|
|
17190
|
+
const match = STANDALONE_SCRIPT_COMMAND.exec(command);
|
|
17191
|
+
const script = match?.[1] ?? match?.[2];
|
|
17192
|
+
return script !== undefined && owned.scripts.has(script);
|
|
17193
|
+
}
|
|
17194
|
+
function userResidual(group, owned, ownership) {
|
|
17195
|
+
if (!isRecord(group) || !Array.isArray(group.hooks)) {
|
|
17196
|
+
return [group];
|
|
17197
|
+
}
|
|
17198
|
+
if (typeof group.description === "string" && ownership.descriptions.has(group.description)) {
|
|
17199
|
+
return [];
|
|
17200
|
+
}
|
|
17201
|
+
const ownedForMatcher = owned?.get(matcherKey(group));
|
|
17202
|
+
const remaining = group.hooks.filter((hook) => !isOwnedCommand(hook, ownedForMatcher));
|
|
17203
|
+
if (remaining.length === group.hooks.length) {
|
|
17204
|
+
return [group];
|
|
17205
|
+
}
|
|
17206
|
+
return remaining.length === 0 ? [] : [{ ...group, hooks: remaining }];
|
|
17207
|
+
}
|
|
17208
|
+
function mergeHookBlocks(existing, generated) {
|
|
17209
|
+
const result = {};
|
|
17210
|
+
for (const [event, groups] of Object.entries(generated)) {
|
|
17211
|
+
result[event] = [...groups];
|
|
17212
|
+
}
|
|
17213
|
+
if (!isRecord(existing)) {
|
|
17214
|
+
return result;
|
|
17215
|
+
}
|
|
17216
|
+
const ownership = collectOwnership(generated);
|
|
17217
|
+
for (const [event, value] of Object.entries(existing)) {
|
|
17218
|
+
if (!Array.isArray(value)) {
|
|
17219
|
+
if (!ownership.events.has(event)) {
|
|
17220
|
+
result[event] = value;
|
|
17221
|
+
}
|
|
17222
|
+
continue;
|
|
17223
|
+
}
|
|
17224
|
+
const owned = ownership.events.get(event);
|
|
17225
|
+
const residual = value.flatMap((group) => userResidual(group, owned, ownership));
|
|
17226
|
+
if (residual.length === 0) {
|
|
17227
|
+
continue;
|
|
17228
|
+
}
|
|
17229
|
+
const current = result[event];
|
|
17230
|
+
result[event] = Array.isArray(current) ? [...current, ...residual] : residual;
|
|
17231
|
+
}
|
|
17232
|
+
return result;
|
|
17233
|
+
}
|
|
17234
|
+
|
|
17235
|
+
// src/core/file-preservation.ts
|
|
16969
17236
|
var DEFAULT_CRITICAL_FILES = ["settings.json", "settings.local.json"];
|
|
16970
17237
|
var DEFAULT_CRITICAL_DIRECTORIES = ["agent-memory", "agent-memory-local"];
|
|
16971
17238
|
var PROTECTED_FRAMEWORK_FILES = ["CLAUDE.md", "AGENTS.md"];
|
|
@@ -17075,12 +17342,19 @@ async function restoreCriticalFiles(rootDir, preservation) {
|
|
|
17075
17342
|
}
|
|
17076
17343
|
return result;
|
|
17077
17344
|
}
|
|
17345
|
+
var HOOKS_MERGE_FILES = new Set(["settings.json", "settings.local.json"]);
|
|
17346
|
+
function isGeneratedHookBlock(value) {
|
|
17347
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) && Object.values(value).every((groups) => Array.isArray(groups));
|
|
17348
|
+
}
|
|
17078
17349
|
async function mergeJsonFile(preservedPath, targetPath) {
|
|
17079
|
-
const preservedData = await
|
|
17350
|
+
const { data: preservedData, format } = parseJsonText(await readTextFile(preservedPath));
|
|
17080
17351
|
if (await fileExists(targetPath)) {
|
|
17081
|
-
const targetData = await
|
|
17352
|
+
const { data: targetData } = parseJsonText(await readTextFile(targetPath));
|
|
17082
17353
|
const merged = deepMerge(targetData, preservedData);
|
|
17083
|
-
|
|
17354
|
+
if (HOOKS_MERGE_FILES.has(basename2(targetPath)) && isGeneratedHookBlock(targetData.hooks)) {
|
|
17355
|
+
merged.hooks = mergeHookBlocks(preservedData.hooks, targetData.hooks);
|
|
17356
|
+
}
|
|
17357
|
+
await writeTextFile(targetPath, stringifyJson(merged, format));
|
|
17084
17358
|
debug("preserve.merged_json", { file: basename2(targetPath) });
|
|
17085
17359
|
} else {
|
|
17086
17360
|
await copyFile(preservedPath, targetPath);
|
|
@@ -17368,14 +17642,14 @@ function rewriteRelativeHookCommand(command) {
|
|
|
17368
17642
|
const interpreter = match[1] ?? "";
|
|
17369
17643
|
return `${interpreter}"\${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/${match[2]}"`;
|
|
17370
17644
|
}
|
|
17371
|
-
function
|
|
17645
|
+
function isRecord2(value) {
|
|
17372
17646
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
17373
17647
|
}
|
|
17374
17648
|
function rewriteNode(node, counter) {
|
|
17375
17649
|
if (Array.isArray(node)) {
|
|
17376
17650
|
return node.map((item) => rewriteNode(item, counter));
|
|
17377
17651
|
}
|
|
17378
|
-
if (!
|
|
17652
|
+
if (!isRecord2(node)) {
|
|
17379
17653
|
return node;
|
|
17380
17654
|
}
|
|
17381
17655
|
const out = {};
|
|
@@ -17392,16 +17666,32 @@ function rewriteNode(node, counter) {
|
|
|
17392
17666
|
}
|
|
17393
17667
|
return out;
|
|
17394
17668
|
}
|
|
17395
|
-
|
|
17396
|
-
|
|
17397
|
-
|
|
17669
|
+
var OLD_STATUSLINE_COMMAND = ".claude/statusline.sh";
|
|
17670
|
+
var NEW_STATUSLINE_COMMAND = `bash "\${CLAUDE_PROJECT_DIR:-.}/.claude/statusline.sh"`;
|
|
17671
|
+
function migrateStatusLine(statusLine) {
|
|
17672
|
+
if (!isRecord2(statusLine) || statusLine.command !== OLD_STATUSLINE_COMMAND) {
|
|
17673
|
+
return null;
|
|
17398
17674
|
}
|
|
17675
|
+
return { ...statusLine, command: NEW_STATUSLINE_COMMAND };
|
|
17676
|
+
}
|
|
17677
|
+
function migrateHookCommands(settings) {
|
|
17399
17678
|
const counter = { count: 0 };
|
|
17400
|
-
const
|
|
17679
|
+
const updates = {};
|
|
17680
|
+
if (isRecord2(settings.hooks)) {
|
|
17681
|
+
const hooks = rewriteNode(settings.hooks, counter);
|
|
17682
|
+
if (counter.count > 0) {
|
|
17683
|
+
updates.hooks = hooks;
|
|
17684
|
+
}
|
|
17685
|
+
}
|
|
17686
|
+
const statusLine = migrateStatusLine(settings.statusLine);
|
|
17687
|
+
if (statusLine !== null) {
|
|
17688
|
+
counter.count++;
|
|
17689
|
+
updates.statusLine = statusLine;
|
|
17690
|
+
}
|
|
17401
17691
|
if (counter.count === 0) {
|
|
17402
17692
|
return { settings, rewritten: 0 };
|
|
17403
17693
|
}
|
|
17404
|
-
return { settings: { ...settings,
|
|
17694
|
+
return { settings: { ...settings, ...updates }, rewritten: counter.count };
|
|
17405
17695
|
}
|
|
17406
17696
|
|
|
17407
17697
|
// src/core/hooks-settings.ts
|
|
@@ -17874,20 +18164,44 @@ function convertHooksJson(hooksJson) {
|
|
|
17874
18164
|
}
|
|
17875
18165
|
return { hooks: result, warnings };
|
|
17876
18166
|
}
|
|
17877
|
-
|
|
17878
|
-
|
|
17879
|
-
|
|
17880
|
-
|
|
18167
|
+
var UTF8_BOM2 = "\uFEFF";
|
|
18168
|
+
function isPlainObject(value) {
|
|
18169
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
18170
|
+
}
|
|
18171
|
+
async function readExistingSettings(settingsPath) {
|
|
18172
|
+
let raw;
|
|
17881
18173
|
try {
|
|
17882
|
-
|
|
17883
|
-
settings = JSON.parse(existingRaw);
|
|
18174
|
+
raw = await readTextFile(settingsPath);
|
|
17884
18175
|
} catch {
|
|
17885
|
-
settings
|
|
18176
|
+
return { ok: true, settings: {}, bom: false };
|
|
18177
|
+
}
|
|
18178
|
+
const bom = raw.startsWith(UTF8_BOM2);
|
|
18179
|
+
if ((bom ? raw.slice(UTF8_BOM2.length) : raw).trim() === "") {
|
|
18180
|
+
return { ok: true, settings: {}, bom };
|
|
18181
|
+
}
|
|
18182
|
+
try {
|
|
18183
|
+
const { data } = parseJsonText(raw);
|
|
18184
|
+
if (isPlainObject(data)) {
|
|
18185
|
+
return { ok: true, settings: data, bom };
|
|
18186
|
+
}
|
|
18187
|
+
return { ok: false, reason: "the top-level JSON value is not a JSON object", bom };
|
|
18188
|
+
} catch (err) {
|
|
18189
|
+
return { ok: false, reason: err instanceof Error ? err.message : String(err), bom };
|
|
17886
18190
|
}
|
|
17887
|
-
|
|
18191
|
+
}
|
|
18192
|
+
async function mergeHooksIntoSettings(settingsPath, hooksJsonPath, options = {}) {
|
|
18193
|
+
const hooksJson = await readJsonFile(hooksJsonPath);
|
|
18194
|
+
const { hooks, warnings } = convertHooksJson(hooksJson);
|
|
18195
|
+
const preserve = options.preserveUserHooks === true;
|
|
18196
|
+
const existing = await readExistingSettings(settingsPath);
|
|
18197
|
+
if (!existing.ok && preserve) {
|
|
18198
|
+
warnings.push(`hooks: could not parse existing ${settingsPath} (${existing.reason}); the file was left unchanged and hooks were NOT installed — fix or move the file, then re-run \`omcustom init\``);
|
|
18199
|
+
return { warnings };
|
|
18200
|
+
}
|
|
18201
|
+
const settings = existing.ok ? existing.settings : {};
|
|
18202
|
+
settings.hooks = preserve ? mergeHookBlocks(settings.hooks, hooks) : hooks;
|
|
17888
18203
|
await ensureDirectory(dirname3(settingsPath));
|
|
17889
|
-
await writeTextFile(settingsPath,
|
|
17890
|
-
`);
|
|
18204
|
+
await writeTextFile(settingsPath, stringifyJson(settings, { bom: existing.bom, trailingNewline: true }));
|
|
17891
18205
|
return { warnings };
|
|
17892
18206
|
}
|
|
17893
18207
|
|
|
@@ -18019,6 +18333,25 @@ async function installStatusline(targetDir, options, _result) {
|
|
|
18019
18333
|
await fs.chmod(destPath, 493);
|
|
18020
18334
|
debug("install.statusline_installed", {});
|
|
18021
18335
|
}
|
|
18336
|
+
async function installSchemas(targetDir, options, _result) {
|
|
18337
|
+
const layout = getProviderLayout();
|
|
18338
|
+
const relPath = join8(layout.rootDir, "schemas", "tool-inputs.json");
|
|
18339
|
+
const srcPath = resolveTemplatePath(relPath);
|
|
18340
|
+
const destPath = join8(targetDir, relPath);
|
|
18341
|
+
if (!await fileExists(srcPath)) {
|
|
18342
|
+
debug("install.schemas_not_found", { path: srcPath });
|
|
18343
|
+
return;
|
|
18344
|
+
}
|
|
18345
|
+
if (await fileExists(destPath)) {
|
|
18346
|
+
if (!options.force && !options.backup) {
|
|
18347
|
+
debug("install.schemas_skipped", { reason: "exists" });
|
|
18348
|
+
return;
|
|
18349
|
+
}
|
|
18350
|
+
}
|
|
18351
|
+
await ensureDirectory(dirname4(destPath));
|
|
18352
|
+
await copyFile(srcPath, destPath);
|
|
18353
|
+
debug("install.schemas_installed", {});
|
|
18354
|
+
}
|
|
18022
18355
|
async function installTestsConfig(targetDir, options, _result) {
|
|
18023
18356
|
const srcPath = resolveTemplatePath(join8("tests", "tsconfig.json"));
|
|
18024
18357
|
const destPath = join8(targetDir, "tests", "tsconfig.json");
|
|
@@ -18035,34 +18368,55 @@ async function installTestsConfig(targetDir, options, _result) {
|
|
|
18035
18368
|
await copyFile(srcPath, destPath);
|
|
18036
18369
|
debug("install.tests_config_installed", {});
|
|
18037
18370
|
}
|
|
18371
|
+
var STATUSLINE_REFRESH_INTERVAL = 10;
|
|
18372
|
+
function updateExistingStatusLine(existing) {
|
|
18373
|
+
const migrated = migrateHookCommands({ statusLine: existing.statusLine });
|
|
18374
|
+
const commandMigrated = migrated.rewritten > 0;
|
|
18375
|
+
if (commandMigrated) {
|
|
18376
|
+
existing.statusLine = migrated.settings.statusLine;
|
|
18377
|
+
}
|
|
18378
|
+
const sl = existing.statusLine;
|
|
18379
|
+
const refreshIntervalAdded = sl.refreshInterval === undefined;
|
|
18380
|
+
if (refreshIntervalAdded) {
|
|
18381
|
+
sl.refreshInterval = STATUSLINE_REFRESH_INTERVAL;
|
|
18382
|
+
}
|
|
18383
|
+
return { commandMigrated, refreshIntervalAdded };
|
|
18384
|
+
}
|
|
18385
|
+
async function mergeStatusLineIntoExisting(settingsPath, existing, format, defaults) {
|
|
18386
|
+
if (!existing.statusLine) {
|
|
18387
|
+
existing.statusLine = defaults.statusLine;
|
|
18388
|
+
await writeTextFile(settingsPath, stringifyJson(existing, format));
|
|
18389
|
+
debug("install.settings_local_merged", {});
|
|
18390
|
+
return;
|
|
18391
|
+
}
|
|
18392
|
+
const { commandMigrated, refreshIntervalAdded } = updateExistingStatusLine(existing);
|
|
18393
|
+
if (!commandMigrated && !refreshIntervalAdded) {
|
|
18394
|
+
debug("install.settings_local_skipped", { reason: "statusLine exists" });
|
|
18395
|
+
return;
|
|
18396
|
+
}
|
|
18397
|
+
await writeTextFile(settingsPath, stringifyJson(existing, format));
|
|
18398
|
+
if (commandMigrated) {
|
|
18399
|
+
debug("install.settings_local_statusline_migrated", {});
|
|
18400
|
+
}
|
|
18401
|
+
if (refreshIntervalAdded) {
|
|
18402
|
+
debug("install.settings_local_refreshInterval_added", {});
|
|
18403
|
+
}
|
|
18404
|
+
}
|
|
18038
18405
|
async function installSettingsLocal(targetDir, result) {
|
|
18039
18406
|
const layout = getProviderLayout();
|
|
18040
18407
|
const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
|
|
18041
18408
|
const statusLineConfig = {
|
|
18042
18409
|
statusLine: {
|
|
18043
18410
|
type: "command",
|
|
18044
|
-
command:
|
|
18411
|
+
command: NEW_STATUSLINE_COMMAND,
|
|
18045
18412
|
padding: 0,
|
|
18046
|
-
refreshInterval:
|
|
18413
|
+
refreshInterval: STATUSLINE_REFRESH_INTERVAL
|
|
18047
18414
|
}
|
|
18048
18415
|
};
|
|
18049
18416
|
if (await fileExists(settingsPath)) {
|
|
18050
18417
|
try {
|
|
18051
|
-
const existing = await
|
|
18052
|
-
|
|
18053
|
-
existing.statusLine = statusLineConfig.statusLine;
|
|
18054
|
-
await writeJsonFile(settingsPath, existing);
|
|
18055
|
-
debug("install.settings_local_merged", {});
|
|
18056
|
-
} else {
|
|
18057
|
-
const sl = existing.statusLine;
|
|
18058
|
-
if (sl.refreshInterval === undefined) {
|
|
18059
|
-
sl.refreshInterval = statusLineConfig.statusLine.refreshInterval;
|
|
18060
|
-
await writeJsonFile(settingsPath, existing);
|
|
18061
|
-
debug("install.settings_local_refreshInterval_added", {});
|
|
18062
|
-
} else {
|
|
18063
|
-
debug("install.settings_local_skipped", { reason: "statusLine exists" });
|
|
18064
|
-
}
|
|
18065
|
-
}
|
|
18418
|
+
const { data: existing, format } = parseJsonText(await readTextFile(settingsPath));
|
|
18419
|
+
await mergeStatusLineIntoExisting(settingsPath, existing, format, statusLineConfig);
|
|
18066
18420
|
} catch {
|
|
18067
18421
|
result.warnings.push("Failed to parse existing settings.local.json, skipping statusLine config");
|
|
18068
18422
|
}
|
|
@@ -18080,7 +18434,9 @@ async function installHooksSettings(targetDir, _options, result) {
|
|
|
18080
18434
|
return;
|
|
18081
18435
|
}
|
|
18082
18436
|
try {
|
|
18083
|
-
const mergeResult = await mergeHooksIntoSettings(settingsPath, hooksJsonPath
|
|
18437
|
+
const mergeResult = await mergeHooksIntoSettings(settingsPath, hooksJsonPath, {
|
|
18438
|
+
preserveUserHooks: true
|
|
18439
|
+
});
|
|
18084
18440
|
if (mergeResult && Array.isArray(mergeResult.warnings) && mergeResult.warnings.length > 0) {
|
|
18085
18441
|
result.warnings.push(...mergeResult.warnings);
|
|
18086
18442
|
}
|
|
@@ -18173,6 +18529,7 @@ async function install(options) {
|
|
|
18173
18529
|
await verifyTemplateDirectory();
|
|
18174
18530
|
await installAllComponents(options.targetDir, options, result);
|
|
18175
18531
|
await installStatusline(options.targetDir, options, result);
|
|
18532
|
+
await installSchemas(options.targetDir, options, result);
|
|
18176
18533
|
await installTestsConfig(options.targetDir, options, result);
|
|
18177
18534
|
await installSettingsLocal(options.targetDir, result);
|
|
18178
18535
|
await installHooksSettings(options.targetDir, options, result);
|
|
@@ -18430,7 +18787,7 @@ async function generateMCPConfig(targetDir) {
|
|
|
18430
18787
|
await writeFile2(mcpConfigPath, `${JSON.stringify(config, null, 2)}
|
|
18431
18788
|
`);
|
|
18432
18789
|
}
|
|
18433
|
-
info("
|
|
18790
|
+
info("mcp.ontology_rag_configured");
|
|
18434
18791
|
}
|
|
18435
18792
|
|
|
18436
18793
|
// src/core/ontology-rag-setup.ts
|
|
@@ -19660,7 +20017,7 @@ async function initCommand(options) {
|
|
|
19660
20017
|
}
|
|
19661
20018
|
|
|
19662
20019
|
// src/cli/list.ts
|
|
19663
|
-
import { basename as basename5, dirname as
|
|
20020
|
+
import { basename as basename5, dirname as dirname5, join as join14, relative as relative3 } from "node:path";
|
|
19664
20021
|
init_fs();
|
|
19665
20022
|
var ALLOWED_TOP_LEVEL_KEYS = new Set(["name", "type", "description", "version", "category"]);
|
|
19666
20023
|
function parseKeyValue(line) {
|
|
@@ -19861,7 +20218,7 @@ async function getSkills(targetDir, rootDir = ".claude", config) {
|
|
|
19861
20218
|
const customSkillPaths = new Set(customComponents.filter((c) => c.type === "skill").map((c) => c.path));
|
|
19862
20219
|
const skillMdFiles = await listFiles(skillsDir, { recursive: true, pattern: "SKILL.md" });
|
|
19863
20220
|
const skills = await Promise.all(skillMdFiles.map(async (skillMdPath) => {
|
|
19864
|
-
const skillDir =
|
|
20221
|
+
const skillDir = dirname5(skillMdPath);
|
|
19865
20222
|
const indexYamlPath = join14(skillDir, "index.yaml");
|
|
19866
20223
|
const { description, version } = await tryReadIndexYamlMetadata(indexYamlPath);
|
|
19867
20224
|
const relativePath = relative3(targetDir, skillDir);
|
|
@@ -21061,11 +21418,12 @@ async function migrateHookCommandsInSettingsLocal(targetDir, options) {
|
|
|
21061
21418
|
});
|
|
21062
21419
|
}
|
|
21063
21420
|
}
|
|
21064
|
-
async function runFullUpdatePostProcessing(options, result, config) {
|
|
21421
|
+
async function runFullUpdatePostProcessing(options, result, config, customizations) {
|
|
21065
21422
|
const isFullUpdate = !options.components || options.components.length === 0;
|
|
21066
21423
|
if (isFullUpdate) {
|
|
21067
|
-
const synced = await syncRootLevelFiles(options.targetDir, options);
|
|
21424
|
+
const { synced, preserved } = await syncRootLevelFiles(options.targetDir, options, customizations);
|
|
21068
21425
|
result.syncedRootFiles = synced;
|
|
21426
|
+
result.preservedFiles.push(...preserved);
|
|
21069
21427
|
const removed = await removeDeprecatedFiles(options.targetDir, options);
|
|
21070
21428
|
result.removedDeprecatedFiles = removed;
|
|
21071
21429
|
if (!options.dryRun) {
|
|
@@ -21182,7 +21540,7 @@ async function update(options) {
|
|
|
21182
21540
|
const lockfile = await readLockfile(options.targetDir);
|
|
21183
21541
|
const components = options.components || getAllUpdateComponents();
|
|
21184
21542
|
await updateAllComponents(options.targetDir, components, updateCheck, customizations, options, result, config, lockfile);
|
|
21185
|
-
await runFullUpdatePostProcessing(options, result, config);
|
|
21543
|
+
await runFullUpdatePostProcessing(options, result, config, customizations);
|
|
21186
21544
|
await regenerateLockfile(options.targetDir, result);
|
|
21187
21545
|
checkAndInstallRtkAfterUpdate();
|
|
21188
21546
|
checkAndInstallCodexAfterUpdate();
|
|
@@ -21373,19 +21731,31 @@ async function updateComponent(targetDir, component, customizations, options, co
|
|
|
21373
21731
|
});
|
|
21374
21732
|
return preservedFiles;
|
|
21375
21733
|
}
|
|
21376
|
-
var ROOT_LEVEL_FILES = [
|
|
21377
|
-
|
|
21734
|
+
var ROOT_LEVEL_FILES = [
|
|
21735
|
+
"statusline.sh",
|
|
21736
|
+
"install-hooks.sh",
|
|
21737
|
+
"uninstall-hooks.sh",
|
|
21738
|
+
"schemas/tool-inputs.json"
|
|
21739
|
+
];
|
|
21740
|
+
async function syncRootLevelFiles(targetDir, options, customizations) {
|
|
21378
21741
|
if (options.dryRun) {
|
|
21379
|
-
return ROOT_LEVEL_FILES;
|
|
21742
|
+
return { synced: ROOT_LEVEL_FILES, preserved: [] };
|
|
21380
21743
|
}
|
|
21381
21744
|
const fs = await import("node:fs/promises");
|
|
21382
21745
|
const layout = getProviderLayout();
|
|
21383
21746
|
const synced = [];
|
|
21747
|
+
const preserved = [];
|
|
21748
|
+
const preserveSet = new Set(customizations && !options.forceOverwriteAll ? customizations.preserveFiles : []);
|
|
21384
21749
|
for (const fileName of ROOT_LEVEL_FILES) {
|
|
21385
21750
|
const srcPath = resolveTemplatePath(join18(layout.rootDir, fileName));
|
|
21386
21751
|
if (!await fileExists(srcPath)) {
|
|
21387
21752
|
continue;
|
|
21388
21753
|
}
|
|
21754
|
+
const relPath = `${layout.rootDir}/${fileName}`;
|
|
21755
|
+
if (preserveSet.has(relPath)) {
|
|
21756
|
+
preserved.push(relPath);
|
|
21757
|
+
continue;
|
|
21758
|
+
}
|
|
21389
21759
|
const destPath = join18(targetDir, layout.rootDir, fileName);
|
|
21390
21760
|
await ensureDirectory(join18(destPath, ".."));
|
|
21391
21761
|
await fs.copyFile(srcPath, destPath);
|
|
@@ -21397,7 +21767,7 @@ async function syncRootLevelFiles(targetDir, options) {
|
|
|
21397
21767
|
if (synced.length > 0) {
|
|
21398
21768
|
debug("update.root_files_synced", { files: synced.join(", ") });
|
|
21399
21769
|
}
|
|
21400
|
-
return synced;
|
|
21770
|
+
return { synced, preserved };
|
|
21401
21771
|
}
|
|
21402
21772
|
async function removeDeprecatedFiles(targetDir, options) {
|
|
21403
21773
|
const manifestPath = resolveTemplatePath("deprecated-files.json");
|