claudecode-omc 5.6.0 → 5.6.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.
@@ -0,0 +1,196 @@
1
+ # Wikipedia "Signs of AI writing" — 本地工作清单
2
+
3
+ 本文件改写自 Wikipedia WikiProject AI Cleanup 维护的 *Signs of AI writing*:
4
+ <https://en.wikipedia.org/wiki/Wikipedia:Signs_of_AI_writing>
5
+
6
+ 每次执行 de-ai-writing 都必须按 Group A → D 走一遍,**逐组打勾**判断哪些信号在响。
7
+ 不要把所有命中都改写,挑 3–5 条最强的下手。
8
+
9
+ ---
10
+
11
+ ## 使用方式
12
+
13
+ 1. 读完原文一遍,先不要急着改。
14
+ 2. 沿 A→D 顺序扫描,每组在心里打√/×,命中就抄一句原文片段做证据。
15
+ 3. 把命中数量收敛到 3–5 条最强信号,写进 `## AI味诊断`。
16
+ 4. 改写时只针对这些信号下手。
17
+ 5. 改完再用同一份清单回扫一次,确认没有把信号搬到新位置。
18
+
19
+ > 本地清单是 Wikipedia 分类法的快照(截至本仓库更新时)。
20
+ > 如果 Wikipedia 上游列出了新条目,应回写到这个文件,而不是在 SKILL.md 里堆。
21
+
22
+ ---
23
+
24
+ ## Group A — 内容层(Content)
25
+
26
+ 围绕“**说什么**”出问题:把对象说得过于重要、列罗辑而非分析、用模糊归因装作权威。
27
+
28
+ ### A1. Inflated significance(虚高重要性)
29
+ - 信号:随手给对象贴“开创性 / 革命性 / 里程碑 / 标志着新时代 / 深远影响”。
30
+ - 中文等价:极具开创性意义、具有划时代地位、深刻改变了……格局。
31
+ - 改写动作:删形容词;用具体动作或后果代替(比如“把延迟从 600ms 降到 80ms”)。
32
+
33
+ ### A2. Notability listing(罗列“知名度”)
34
+ - 信号:堆作品、奖项、合作方来证明对象“值得写”。
35
+ - 中文等价:曾参与/合作/出席/受邀……(一长串)。
36
+ - 改写动作:保留与论点相关的 1–2 项;把剩下的删掉,不要换成“等”。
37
+
38
+ ### A3. "-ing" superficial analysis(动名词式表面分析)
39
+ - 信号:英文里 “highlighting…, showcasing…, reflecting…, underscoring…”。
40
+ - 中文等价:……展示了……、……体现了……、……彰显了……、……折射出……。
41
+ - 改写动作:把“展示/体现/折射”换成具体机制:是谁、做了什么、产生了什么后果。
42
+
43
+ ### A4. Promotional language(公关腔)
44
+ - 信号:广告/营销页式赞美。
45
+ - 中文等价:堪称行业标杆、用户口碑爆棚、完美解决、极致体验、强强联合。
46
+ - 改写动作:换成限定(“在 X 场景下表现稳定”),允许说出短板。
47
+
48
+ ### A5. Vague attribution(模糊归因)
49
+ - 信号:“some critics say / scholars argue / it is widely believed”。
50
+ - 中文等价:业界普遍认为、有评论指出、不少专家表示、众所周知。
51
+ - 改写动作:能指名就指名,不能就直接陈述判断 + 证据;删“众所周知”。
52
+
53
+ ### A6. Outline-style “challenges and future”(机械化的“挑战与展望”)
54
+ - 信号:“面临的挑战 / 未来展望 / 启示与思考”这种 AI 收尾标配。
55
+ - 改写动作:要么不写这一段;要么改成具体悬而未决的问题,不要做万金油总结。
56
+
57
+ ---
58
+
59
+ ## Group B — 语言层(Language)
60
+
61
+ 围绕“**怎么遣词**”出问题:高密度 AI 偏好词、为避免“是”而扭曲句法、对偶/三段式上瘾。
62
+
63
+ ### B1. AI vocabulary density(AI 偏好词高密度)
64
+ 英文高频:delve, tapestry, navigate, leverage, robust, seamless, holistic, foster, paramount, intricate, nuance, embark, journey, realm, landscape, ever-evolving, transformative。
65
+
66
+ 中文高频(必查):
67
+ - 赋能、抓手、闭环、生态、链路、底层逻辑、心智、范式
68
+ - 极简、极致、深度、全面、系统化、结构化
69
+ - 拥抱、洞察、解码、激发、点亮、照亮、释放
70
+ - 旅程、画卷、版图、宇宙、生态位
71
+ - 不仅……更……、既……又……、不止于……
72
+
73
+ > 单独出现 1–2 个不算问题;密度上来或扎堆同段就要清。
74
+
75
+ ### B2. Copulative avoidance(回避“是”/“有”)
76
+ - 信号:刻意不用最简单的连接动词,用各种花式替代。
77
+ - 中文等价:过度使用“构成 / 呈现出 / 彰显出 / 演绎着 / 诠释为”代替“是”。
78
+ - 改写动作:能用“是 / 不是 / 有 / 没有”就直接用。
79
+
80
+ ### B3. Negative parallelism(负向平行)
81
+ - 信号:“not just X, but Y” 的反复使用。
82
+ - 中文等价:不仅是……更是……、不只是……而是……、与其说是……不如说是……。
83
+ - 改写动作:选一个角度直接说;不要为了对仗硬凑后半句。
84
+
85
+ ### B4. Rule of three(三段式上瘾)
86
+ - 信号:三个并列形容词、三段式列举、ABC 三步法。
87
+ - 中文等价:清晰、准确、有力;快速、稳定、可控;本质上是……、根本上是……、最终是……。
88
+ - 改写动作:改成两项或四项;或把三项中重复的删掉。
89
+
90
+ ### B5. Elegant variation(雅致替换)
91
+ - 信号:同一概念在一段内被换三种说法假装“不重复”。
92
+ - 中文等价:一段里 “模型 / 系统 / 框架 / 方案” 互换指同一对象。
93
+ - 改写动作:除非语义真不同,否则统一称呼;专业语境下重复术语 ≠ 啰嗦。
94
+
95
+ ---
96
+
97
+ ## Group C — 形式与版面(Style & Format)
98
+
99
+ 围绕“**怎么排版**”出问题:标点过度、加粗满天飞、模板化排版。
100
+
101
+ ### C1. Em-dash overuse(破折号上瘾)
102
+ - 信号:每段都靠 `——` 制造“揭示感”。
103
+ - 改写动作:保留 1 处真正必要的;其余改逗号、句号或拆句。
104
+
105
+ ### C2. Bold overuse(加粗滥用)
106
+ - 信号:把整句、整段重要观点全加粗,导致“没有重点”。
107
+ - 改写动作:一段最多 1 个加粗;非术语 / 非关键名词不加粗。
108
+
109
+ ### C3. Inline-header lists(行内伪标题)
110
+ - 信号:列表每条以 `**关键词:**` 开头,全文像模板填空。
111
+ - 改写动作:要么改成正常段落叙述;要么允许长度不一的自由项。
112
+
113
+ ### C4. Emoji bullets(emoji 项目符号)
114
+ - 信号:✅⚡🚀✨ 当作小标题或列表点。
115
+ - 改写动作:技术稿/讲道稿默认全删;公众号稿保留 ≤2 个有功能性的。
116
+
117
+ ### C5. Curly quotes / 全角混乱
118
+ - 信号:英文里出现 Word 风格 “…” 智能引号、全角半角混排。
119
+ - 改写动作:统一为目标语种的标准引号。中文用「」或“”,不要混。
120
+
121
+ ### C6. Title-case headings(英文标题滥用 Title Case)
122
+ - 信号:英文小标题里每个词首字母大写。
123
+ - 中文等价:所有小标题都做成名词短语 + 冒号开头的对仗结构。
124
+ - 改写动作:允许小标题长度参差;不要强求节奏。
125
+
126
+ ---
127
+
128
+ ## Group D — 沟通姿态(Communication)
129
+
130
+ 围绕“**对读者讲话的姿态**”出问题:讨好、套话收尾、过度限定。
131
+
132
+ ### D1. Sycophancy(讨好)
133
+ - 信号:“Great question!”、“你这个问题问得非常关键”。
134
+ - 中文等价:你说得太对了、这个问题确实非常值得思考、感谢你提出这么好的问题。
135
+ - 改写动作:直接进入回答;删讨好句。
136
+
137
+ ### D2. Canned closures(罐头收尾)
138
+ - 信号:In conclusion, To sum up, In summary, Hope this helps。
139
+ - 中文等价:综上所述、总而言之、希望这能帮到你、以上就是关于……的全部内容。
140
+ - 改写动作:删;如果真需要收束,改成一个判断句或一个未解的问题。
141
+
142
+ ### D3. Knowledge-cutoff disclaimers(知识截止时间声明)
143
+ - 信号:“截至我的最新知识 / 截至 20XX 年 X 月 / 据我所知最新情况”。
144
+ - 改写动作:除非用户明确要求声明数据时效,否则删。
145
+
146
+ ### D4. Filler phrases(空填充)
147
+ - 信号:“值得注意的是、需要指出的是、不可忽视的是、众所周知、毋庸置疑”。
148
+ - 改写动作:删。要表达“注意”可以直接给那句结论。
149
+
150
+ ### D5. Over-qualification(过度限定)
151
+ - 信号:一句话里堆 3+ 个软化词:“可能、也许、在某种程度上、相对而言、通常来说”。
152
+ - 改写动作:保留 ≤1 个限定;不确定就改成承认范围(“在 X 条件下成立”)而不是“可能也许大概”。
153
+
154
+ ---
155
+
156
+ ## 快速回扫表(改完后用)
157
+
158
+ 按住下面这张表把改写稿再过一次,每条问“这条还在吗”:
159
+
160
+ | Group | 信号 | 改完后是否还在 |
161
+ |-------|------|----------------|
162
+ | A1 | 虚高重要性形容词 | ☐ |
163
+ | A2 | 知名度堆列 | ☐ |
164
+ | A3 | -ing / 展示体现折射 | ☐ |
165
+ | A4 | 公关腔 | ☐ |
166
+ | A5 | 模糊归因 | ☐ |
167
+ | A6 | 挑战/展望模板 | ☐ |
168
+ | B1 | AI 偏好词扎堆 | ☐ |
169
+ | B2 | 回避“是” | ☐ |
170
+ | B3 | 负向平行 | ☐ |
171
+ | B4 | 三段式 | ☐ |
172
+ | B5 | 雅致替换 | ☐ |
173
+ | C1 | 破折号过载 | ☐ |
174
+ | C2 | 加粗满天飞 | ☐ |
175
+ | C3 | 行内伪标题列表 | ☐ |
176
+ | C4 | emoji 项目符号 | ☐ |
177
+ | C5 | 引号 / 全角混乱 | ☐ |
178
+ | C6 | Title-case / 对仗小标题 | ☐ |
179
+ | D1 | 讨好开场 | ☐ |
180
+ | D2 | 罐头收尾 | ☐ |
181
+ | D3 | 知识截止声明 | ☐ |
182
+ | D4 | 空填充 | ☐ |
183
+ | D5 | 过度限定 | ☐ |
184
+
185
+ 如果回扫表里仍有 ≥3 项被标记,说明改写不彻底,需要再做一轮。
186
+
187
+ ---
188
+
189
+ ## 与中文清单的关系
190
+
191
+ - 本文件是**判断层**:决定“哪些 AI 信号在响”。
192
+ - `chinese-patterns.md` 是**改写层**:给出中文具体套话的替换写法。
193
+ - `chinese-signals.md` 是**词频层**:高频 AI 词典,做扫描用。
194
+ - `domain-playbooks.md` 是**领域护栏**:技术稿 / 讲道稿不能动哪些。
195
+
196
+ 调用顺序:本文件先用 → chinese-signals 做词频比对 → chinese-patterns 找替换 → domain-playbooks 复核保护项。
package/README.md CHANGED
@@ -13,6 +13,8 @@ omc-manage setup
13
13
 
14
14
  ## What Gets Installed
15
15
 
16
+ Defaults (only the bundled sources, no extras):
17
+
16
18
  | Artifact | Count | Sources |
17
19
  |----------|-------|---------|
18
20
  | Skills | ~62 | oh-my-claudecode + superpowers |
@@ -22,6 +24,9 @@ omc-manage setup
22
24
  | Guidelines | 1 | local coding discipline prompt guidelines |
23
25
 
24
26
  All artifacts are installed to `~/.claude/` where Claude Code discovers them automatically.
27
+ Adding a curated subset of [everything-claude-code](https://github.com/affaan-m/everything-claude-code)
28
+ on top can take totals to ~94 skills / ~35 agents / ~26 commands — see
29
+ [Distribution-Repo Sources](#distribution-repo-sources) below.
25
30
 
26
31
  The bundled guidelines install into `~/.claude/CLAUDE.md` and add lightweight
27
32
  coding discipline rules
@@ -33,14 +38,15 @@ completion with concrete evidence.
33
38
 
34
39
  | Command | Description |
35
40
  |---------|-------------|
36
- | `omc-manage setup [--force] [--dry-run] [--type <type>]` | Install merged artifacts |
37
- | `omc-manage doctor` | Health checks |
41
+ | `omc-manage setup [--force] [--dry-run] [--type <type>] [--source <name>]` | Install merged artifacts |
42
+ | `omc-manage doctor` | Health checks; reports each source's `kind`, `profile`, and `allowlist`, and flags `staged` distribution sources awaiting `plan apply` |
38
43
  | `omc-manage source list` | Show configured sources |
39
- | `omc-manage source sync` | Update upstream sources to latest |
40
- | `omc-manage source add <name> <url>` | Add a new source, including `guidelines` sources |
44
+ | `omc-manage source sync [<name>]` | Update upstream sources to latest |
45
+ | `omc-manage source add <name> <url> [--kind ...] [--artifacts ...] [--manifests ...] [--profiles ...]` | Add a new source, including `guidelines` and `distribution-repo` sources |
46
+ | `omc-manage source remove <name>` | Remove a registered source |
41
47
  | `omc-manage source inspect <name>` | Inspect a source as a bundle/catalog instead of only as flat artifacts |
42
48
  | `omc-manage plan install <source> --profile <name>` | Build a profile-driven install plan for a source |
43
- | `omc-manage plan apply <source> --profile <name>` | Materialize a reviewed plan into source activation state |
49
+ | `omc-manage plan apply <source> --profile <name> [--selection-file <path>]` | Materialize a reviewed plan into source activation state, optionally curating an item-level allowlist |
44
50
  | `omc-manage artifact list [--type <type>]` | List merged artifacts |
45
51
  | `omc-manage artifact conflicts [--type <type>]` | Show conflict report |
46
52
  | `omc-manage guidelines optimize [source...]` | Build maintainer-only guideline optimization artifacts |
@@ -48,13 +54,15 @@ completion with concrete evidence.
48
54
 
49
55
  ## Sources & Priority
50
56
 
51
- | Source | Priority | Description |
52
- |--------|----------|-------------|
53
- | local | 1 (highest) | Your custom artifacts in `~/.omc-manage/local/` |
54
- | oh-my-claudecode | 2 | Multi-agent orchestration framework |
55
- | superpowers | 3 | Engineering process guardrails (TDD, debugging, etc.) |
57
+ | Source | Priority | Default? | Description |
58
+ |--------|----------|----------|-------------|
59
+ | local | 1 (highest) | yes | Your custom artifacts in `~/.omc-manage/local/` |
60
+ | oh-my-claudecode | 2 | yes | Multi-agent orchestration framework |
61
+ | superpowers | 3 | yes | Engineering process guardrails (TDD, debugging, etc.) |
62
+ | ecc / your own | 4+ | opt-in | Distribution-style repos added via `source add --kind distribution-repo` |
56
63
 
57
- Local artifacts always win conflicts. Add your own skills:
64
+ Local artifacts always win conflicts. Sources added via `source add` are
65
+ appended at the next free priority. Add your own skills:
58
66
 
59
67
  ```bash
60
68
  mkdir -p ~/.omc-manage/local/skills/my-skill
@@ -73,10 +81,16 @@ omc-manage source sync karpathy
73
81
  omc-manage setup --type guidelines
74
82
  ```
75
83
 
76
- Add a distribution-style source such as `everything-claude-code` without
77
- blindly installing its full multi-harness surface:
84
+ <a id="distribution-repo-sources"></a>
85
+ ### Distribution-Repo Sources
86
+
87
+ Distribution repos (e.g. [everything-claude-code](https://github.com/affaan-m/everything-claude-code))
88
+ publish multiple harness surfaces, manifests, and reference material alongside
89
+ installable Claude artifacts. OMC absorbs them in four stages so you can pick
90
+ exactly what reaches `~/.claude/`:
78
91
 
79
92
  ```bash
93
+ # 1. Register — sync the full repo into project metadata, install nothing yet.
80
94
  omc-manage source add ecc https://github.com/affaan-m/everything-claude-code.git \
81
95
  --artifacts skills,agents,hooks,commands \
82
96
  --kind distribution-repo \
@@ -84,41 +98,59 @@ omc-manage source add ecc https://github.com/affaan-m/everything-claude-code.git
84
98
  --harnesses claude,codex,cursor,gemini,opencode \
85
99
  --manifests package.json,.claude-plugin/plugin.json,agent.yaml \
86
100
  --profiles claude-runtime,reference-only
101
+
102
+ # 2. Sync — clones into .upstream/<source>/ and reads declared manifests.
87
103
  omc-manage source sync ecc
104
+
105
+ # 3. Inspect — normalized catalog of runtime / harness / reference surfaces.
88
106
  omc-manage source inspect ecc
107
+
108
+ # 4. Plan — preview what a profile would activate.
89
109
  omc-manage plan install ecc --profile claude-runtime
90
- omc-manage plan apply ecc --profile claude-runtime
91
110
  ```
92
111
 
93
- `source inspect` reads synced manifests and produces a normalized catalog of
94
- runtime, reference, tooling, and harness-specific surfaces. `plan install`
95
- turns that catalog into a profile-driven plan so OMC can absorb
96
- distribution-style repositories incrementally instead of treating every repo as
97
- flat artifact directories. `plan apply` is the next gate: it changes the
98
- source's activation state in OMC config and writes an audit record under source
99
- metadata, but it still works at source/profile granularity rather than item
100
- whitelisting.
112
+ At this point nothing has been installed. `doctor` shows the source as
113
+ `staged, run "omc-manage plan apply <name>"` so you don't lose track of it.
114
+
115
+ To activate, choose one of:
116
+
117
+ ```bash
118
+ # All runtime artifacts the profile selected (no item-level curation):
119
+ omc-manage plan apply ecc --profile claude-runtime
120
+
121
+ # Reference-only — keep the repo synced locally, install nothing:
122
+ omc-manage plan apply ecc --profile reference-only
101
123
 
102
- For item-level curation, pass a selection file to `plan apply`:
124
+ # Curated subset via selection file (recommended for large repos):
125
+ omc-manage plan apply ecc \
126
+ --profile claude-runtime \
127
+ --selection-file /absolute/path/to/selection.json
128
+ ```
129
+
130
+ A selection file is a per-artifact-type allowlist:
103
131
 
104
132
  ```json
105
133
  {
106
- "skills": ["tdd-workflow", "verification-loop"],
107
- "agents": ["planner", "architect"]
134
+ "skills": ["agent-eval", "santa-method", "prompt-optimizer"],
135
+ "agents": ["harness-optimizer", "opensource-sanitizer"],
136
+ "commands": ["prp-prd", "prp-plan", "harness-audit"]
108
137
  }
109
138
  ```
110
139
 
111
- Then apply it:
140
+ When the synced source exposes an item directory, OMC validates the names
141
+ against the catalog; for manifest-only surfaces it accepts the allowlist with a
142
+ warning and the real filtering happens in `setup` against on-disk content. The
143
+ allowlist becomes part of the source config in `~/.omc-manage/sources.json` and
144
+ is enforced on every subsequent `omc-manage setup`.
145
+
146
+ After `plan apply`, finalize with:
112
147
 
113
148
  ```bash
114
- omc-manage plan apply ecc \
115
- --profile claude-runtime \
116
- --selection-file /absolute/path/to/selection.json
149
+ omc-manage setup --dry-run --source ecc # confirm scope
150
+ omc-manage setup --source ecc # install only this source
151
+ omc-manage doctor # verify allowlist counts
117
152
  ```
118
153
 
119
- When present, the selection file becomes a source-level `allowlist`. OMC will
120
- only merge those named items from that source for the selected artifact types.
121
-
122
154
  ## Maintainer Guideline Optimization
123
155
 
124
156
  OMC now treats runtime prompt guidance as a first-class `guidelines` artifact,
@@ -171,19 +203,16 @@ When the same artifact exists in multiple sources:
171
203
  3. **Local priority** — local always wins
172
204
  4. **Source priority** — lower priority number wins
173
205
 
174
- ## Source Bundles
175
-
176
- OMC now distinguishes between two source kinds:
206
+ ## Source Kinds
177
207
 
178
- - `content-repo` a repo already shaped like OMC artifact directories
179
- - `distribution-repo` — a repo that publishes multiple harness surfaces,
180
- manifests, scripts, and reference material alongside installable Claude
181
- runtime artifacts
208
+ OMC distinguishes two source shapes:
182
209
 
183
- For distribution repos, OMC syncs declared manifest files into source metadata,
184
- builds a normalized catalog, and lets you plan by profile before deciding what
185
- to install. Distribution repos default to `installMode=planned`, so ordinary
186
- `setup` and `artifact list` flows do not absorb them automatically.
210
+ - `content-repo` already shaped like OMC artifact directories (`skills/`,
211
+ `agents/`, etc.); installed as-is.
212
+ - `distribution-repo` publishes multiple harness surfaces, manifests, and
213
+ reference material; defaults to `installMode=planned` so ordinary `setup`
214
+ and `artifact list` flows do not absorb it automatically. Activate it
215
+ through the [Distribution-Repo Sources](#distribution-repo-sources) flow.
187
216
 
188
217
  ## License
189
218
 
@@ -1,5 +1,5 @@
1
1
  {
2
- "bundledAt": "2026-04-27T11:15:45Z",
2
+ "bundledAt": "2026-04-30T02:34:50Z",
3
3
  "sources": {
4
4
  "anthropic-skills": { "artifacts": 2 },
5
5
  "ecc": { "artifacts": 131 },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claudecode-omc",
3
- "version": "5.6.0",
3
+ "version": "5.6.2",
4
4
  "description": "Claude Code harness — best-practice skills, agents, hooks, and configs from multiple sources",
5
5
  "bin": {
6
6
  "omc-manage": "bin/omc-manage.js"
@@ -15,6 +15,7 @@
15
15
  "README.md"
16
16
  ],
17
17
  "scripts": {
18
+ "test": "node --test",
18
19
  "setup": "node bin/omc-manage.js setup",
19
20
  "doctor": "node bin/omc-manage.js doctor",
20
21
  "guidelines:optimize": "node bin/omc-manage.js guidelines optimize",
@@ -1,70 +1,10 @@
1
1
  /* eslint-disable no-console */
2
2
  const fs = require('fs');
3
3
  const fsp = require('fs/promises');
4
- const path = require('path');
5
- const { getProjectRoot, getSourceArtifactDir, getMergeConfigPath, getReportDir } = require('../config/paths');
6
- const { readConfig, filterItemsByAllowlist } = require('../config/sources');
4
+ const { getProjectRoot, getMergeConfigPath } = require('../config/paths');
7
5
  const { ARTIFACT_TYPES, getArtifactTypeNames } = require('../config/artifact-types');
8
6
  const { detectConflicts, resolveConflicts, applyResolutions, generateReport } = require('../merge/base-merger');
9
- const { loadSkillsFromSource } = require('../merge/skill-merger');
10
- const { loadAgentsFromSource } = require('../merge/agent-merger');
11
- const { loadCommandsFromSource } = require('../merge/command-merger');
12
- const { loadHookFilesFromSource } = require('../merge/hook-merger');
13
- const { loadFilesFromSource } = require('../merge/file-merger');
14
- const { loadClaudeMd } = require('../merge/claude-md-merger');
15
-
16
- function loadSectionDocumentFromSource(sourceDir) {
17
- const content = loadClaudeMd(sourceDir);
18
- if (!content) return [];
19
- return [{
20
- name: 'CLAUDE.md',
21
- path: sourceDir,
22
- metadata: {
23
- description: `${content.length} chars of prompt guidelines`,
24
- },
25
- }];
26
- }
27
-
28
- function getLoader(artifactType) {
29
- switch (artifactType) {
30
- case 'skills': return loadSkillsFromSource;
31
- case 'agents': return loadAgentsFromSource;
32
- case 'commands': return loadCommandsFromSource;
33
- case 'hooks': return loadHookFilesFromSource;
34
- case 'guidelines': return loadSectionDocumentFromSource;
35
- case 'claude-md': return loadSectionDocumentFromSource;
36
- case 'hud': return loadFilesFromSource;
37
- default: return null;
38
- }
39
- }
40
-
41
- function loadSourcesForType(artifactType, root) {
42
- const config = readConfig();
43
- const sources = [];
44
- const ordered = Object.entries(config.sources)
45
- .sort(([, a], [, b]) => a.priority - b.priority);
46
-
47
- const loader = getLoader(artifactType);
48
- if (!loader) return sources;
49
-
50
- for (const [name, src] of ordered) {
51
- if (src.role === 'reference') continue;
52
- if (src.installMode && src.installMode !== 'auto') continue;
53
- if (!(src.artifacts || []).includes(artifactType)) continue;
54
- const dir = getSourceArtifactDir(name, artifactType, root);
55
- if (!fs.existsSync(dir)) continue;
56
- const items = filterItemsByAllowlist(src, artifactType, loader(dir, name));
57
- if (items.length > 0) {
58
- sources.push({ name, items });
59
- }
60
- }
61
-
62
- if (sources.length === 0 && artifactType === 'claude-md') {
63
- return loadSourcesForType('guidelines', root);
64
- }
65
-
66
- return sources;
67
- }
7
+ const { loadSourcesForType } = require('../merge/artifact-source-loader');
68
8
 
69
9
  async function artifact(args, flags = {}) {
70
10
  const cmd = args[0] || 'list';
package/src/cli/setup.js CHANGED
@@ -3,23 +3,17 @@ const fs = require('fs');
3
3
  const fsp = require('fs/promises');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
- const { getProjectRoot, getSourceArtifactDir, getInstallTarget, getMergeConfigPath, getReportDir } = require('../config/paths');
6
+ const { getProjectRoot, getScopedInstallTarget, getMergeConfigPath } = require('../config/paths');
7
7
  const { readConfig, filterItemsByAllowlist } = require('../config/sources');
8
8
  const { getArtifactTypeNames, ARTIFACT_TYPES } = require('../config/artifact-types');
9
- const { detectConflicts, resolveConflicts, applyResolutions, generateReport } = require('../merge/base-merger');
10
- const { loadSkillsFromSource } = require('../merge/skill-merger');
11
- const { loadAgentsFromSource } = require('../merge/agent-merger');
12
- const { loadCommandsFromSource } = require('../merge/command-merger');
13
- const { loadHookFilesFromSource, loadHooksConfig, mergeHooksConfigs, hasHookLib } = require('../merge/hook-merger');
9
+ const { detectConflicts, resolveConflicts, applyResolutions } = require('../merge/base-merger');
10
+ const { loadHooksConfig, mergeHooksConfigs, hasHookLib } = require('../merge/hook-merger');
14
11
  const { loadClaudeMd, mergeIntoExisting, assembleSections } = require('../merge/claude-md-merger');
15
12
  const { loadSettingsFragment, mergeSettingsFragments } = require('../merge/settings-merger');
16
- const { loadFilesFromSource } = require('../merge/file-merger');
17
- const { evaluateSkillQuality } = require('../utils/quality');
18
- const { copyDirRecursive } = require('./source');
13
+ const { collectSourceDirsForType, getArtifactLoader } = require('../merge/artifact-source-loader');
19
14
 
20
15
  const OMC_VERSION_PATH = path.join(os.homedir(), '.claude', '.omc-version.json');
21
16
  const OMC_CONFIG_PATH = path.join(os.homedir(), '.claude', '.omc-config.json');
22
- const OMC_INSTALL_MANIFEST_PATH = path.join(os.homedir(), '.claude', '.omc-install-manifest.json');
23
17
  const LEGACY_HOOK_PATHS = [
24
18
  'hooks.json',
25
19
  'hooks-cursor.json',
@@ -125,6 +119,18 @@ function inferLegacyManagedPaths(artifactType, installTarget, desiredPaths, extr
125
119
  }
126
120
  }
127
121
 
122
+ function normalizePreviousManagedPaths(artifactType, previousPaths) {
123
+ if (!Array.isArray(previousPaths)) return previousPaths;
124
+
125
+ if (artifactType !== 'agents' && artifactType !== 'commands') {
126
+ return previousPaths;
127
+ }
128
+
129
+ return previousPaths.map((relativePath) => (
130
+ path.extname(relativePath) ? relativePath : `${relativePath}.md`
131
+ ));
132
+ }
133
+
128
134
  async function pruneManagedPaths(installTarget, previousPaths, desiredPaths, flags) {
129
135
  if (!fs.existsSync(installTarget) || !fs.statSync(installTarget).isDirectory()) {
130
136
  return 0;
@@ -192,43 +198,19 @@ async function copyDirectory(src, dest, options = {}) {
192
198
  return count;
193
199
  }
194
200
 
195
- function getLoader(artifactType) {
196
- switch (artifactType) {
197
- case 'skills': return loadSkillsFromSource;
198
- case 'agents': return loadAgentsFromSource;
199
- case 'commands': return loadCommandsFromSource;
200
- case 'hooks': return loadHookFilesFromSource;
201
- case 'hud': return loadFilesFromSource;
202
- default: return null;
203
- }
204
- }
205
-
206
- function collectSourcesForType(artifactType, orderedSources, root) {
207
- const sourcesForType = [];
208
-
209
- for (const [name, src] of orderedSources) {
210
- // Skip reference-only sources (e.g. anthropic-skills) — they provide
211
- // evaluation standards, not installable artifacts.
212
- if (src.role === 'reference') continue;
213
- if (src.installMode && src.installMode !== 'auto') continue;
214
- const declaredArtifacts = src.artifacts || [];
215
- if (!declaredArtifacts.includes(artifactType)) continue;
216
-
217
- const dir = getSourceArtifactDir(name, artifactType, root);
218
- if (fs.existsSync(dir)) {
219
- sourcesForType.push({ name, dir, priority: src.priority, config: src });
220
- }
221
- }
222
-
223
- if (sourcesForType.length === 0 && artifactType === 'claude-md') {
224
- return collectSourcesForType('guidelines', orderedSources, root);
201
+ function getManagedPathForItem(artifactType, item) {
202
+ if (artifactType === 'skills' || item.isDirectory) {
203
+ return item.name;
225
204
  }
226
205
 
227
- return sourcesForType;
206
+ // Loaders keep logical names extensionless for conflict checks; manifests
207
+ // must track the real path written to disk.
208
+ const sourceExt = path.extname(item.path);
209
+ return sourceExt && !item.name.endsWith(sourceExt) ? `${item.name}${sourceExt}` : item.name;
228
210
  }
229
211
 
230
212
  async function installNameBasedArtifacts(artifactType, sources, mergeConfig, installTarget, flags) {
231
- const loader = getLoader(artifactType);
213
+ const loader = getArtifactLoader(artifactType);
232
214
  if (!loader) return { count: 0, total: 0 };
233
215
 
234
216
  const loaded = [];
@@ -257,6 +239,8 @@ async function installNameBasedArtifacts(artifactType, sources, mergeConfig, ins
257
239
  }
258
240
  }
259
241
 
242
+ const managedPaths = merged.map(item => getManagedPathForItem(artifactType, item));
243
+
260
244
  if (flags.dryRun) {
261
245
  for (const item of merged.sort((a, b) => a.name.localeCompare(b.name))) {
262
246
  console.log(` ${item.name} (${item.sourceName})`);
@@ -265,7 +249,7 @@ async function installNameBasedArtifacts(artifactType, sources, mergeConfig, ins
265
249
  count: 0,
266
250
  total: merged.length,
267
251
  conflicts: conflicts.length,
268
- managedPaths: merged.map(item => item.name),
252
+ managedPaths,
269
253
  excludedNames: excludeList,
270
254
  };
271
255
  }
@@ -275,14 +259,10 @@ async function installNameBasedArtifacts(artifactType, sources, mergeConfig, ins
275
259
 
276
260
  for (const item of merged) {
277
261
  if (artifactType === 'skills' || item.isDirectory) {
278
- const dest = path.join(installTarget, item.name);
262
+ const dest = path.join(installTarget, getManagedPathForItem(artifactType, item));
279
263
  fileCount += await copyDirectory(item.path, dest, flags);
280
264
  } else {
281
- // Single file copy. Loaders strip `.md` from item.name for matching/allowlist purposes;
282
- // re-attach the source extension on disk so Claude Code's `*.md` loader picks them up.
283
- const sourceExt = path.extname(item.path);
284
- const destName = sourceExt && !item.name.endsWith(sourceExt) ? `${item.name}${sourceExt}` : item.name;
285
- const dest = path.join(installTarget, destName);
265
+ const dest = path.join(installTarget, getManagedPathForItem(artifactType, item));
286
266
  await fsp.mkdir(path.dirname(dest), { recursive: true });
287
267
  await fsp.copyFile(item.path, dest);
288
268
  fileCount += 1;
@@ -293,7 +273,7 @@ async function installNameBasedArtifacts(artifactType, sources, mergeConfig, ins
293
273
  count: fileCount,
294
274
  total: merged.length,
295
275
  conflicts: conflicts.length,
296
- managedPaths: merged.map(item => item.name),
276
+ managedPaths,
297
277
  excludedNames: excludeList,
298
278
  };
299
279
  }
@@ -440,10 +420,6 @@ async function setup(args, flags = {}) {
440
420
  try { mergeConfig = JSON.parse(fs.readFileSync(mergeConfigPath, 'utf8')); } catch {}
441
421
  }
442
422
 
443
- // Get ordered sources (by priority)
444
- const orderedSources = Object.entries(config.sources)
445
- .sort(([, a], [, b]) => a.priority - b.priority);
446
-
447
423
  const allTypes = getArtifactTypeNames().filter(type => type !== 'claude-md');
448
424
  const typesToInstall = typeFilter || allTypes;
449
425
  const previousManifest = await readJsonFile(manifestPath, { artifacts: {} });
@@ -463,11 +439,9 @@ async function setup(args, flags = {}) {
463
439
  continue;
464
440
  }
465
441
 
466
- const sourcesForType = collectSourcesForType(artifactType, orderedSources, root);
442
+ const sourcesForType = collectSourceDirsForType(artifactType, root, config);
467
443
 
468
- const installTarget = (artifactType === 'skills' && scope === 'project')
469
- ? path.join(process.cwd(), '.claude', 'skills')
470
- : typeConfig.installTarget;
444
+ const installTarget = getScopedInstallTarget(artifactType, scope, process.cwd());
471
445
 
472
446
  console.log(`[${step}/${totalSteps}] ${typeConfig.label} (${sourcesForType.length} sources)`);
473
447
 
@@ -501,7 +475,10 @@ async function setup(args, flags = {}) {
501
475
  }
502
476
 
503
477
  if (typeConfig.format !== 'single-file' && typeConfig.format !== 'json') {
504
- const previousPaths = previousManifest.artifacts?.[artifactType]?.paths;
478
+ const previousPaths = normalizePreviousManagedPaths(
479
+ artifactType,
480
+ previousManifest.artifacts?.[artifactType]?.paths,
481
+ );
505
482
  const managedPaths = uniq(result.managedPaths || []);
506
483
  const bootstrapPaths = previousPaths || inferLegacyManagedPaths(
507
484
  artifactType,