claudecode-omc 5.6.1 → 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.
@@ -1,5 +1,5 @@
1
1
  {
2
- "bundledAt": "2026-04-29T02:22:02Z",
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.1",
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,
package/src/cli/source.js CHANGED
@@ -74,8 +74,8 @@ async function syncRemoteSource(sourceName, sourceConfig, root) {
74
74
  const srcPath = path.join(tmpDir, srcSubdir);
75
75
  const destPath = getSyncTargetDir(sourceName, artifactType, root);
76
76
 
77
+ await fsp.rm(destPath, { recursive: true, force: true });
77
78
  if (fs.existsSync(srcPath)) {
78
- await fsp.rm(destPath, { recursive: true, force: true });
79
79
  if (fs.statSync(srcPath).isDirectory()) {
80
80
  await copyDirRecursive(srcPath, destPath);
81
81
  } else {
@@ -213,6 +213,23 @@ function getInstallTarget(artifactType) {
213
213
  return type.installTarget;
214
214
  }
215
215
 
216
+ function getScopedInstallTarget(artifactType, scope = 'user', cwd = process.cwd()) {
217
+ if (scope !== 'project') {
218
+ return getInstallTarget(artifactType);
219
+ }
220
+
221
+ if (artifactType === 'guidelines' || artifactType === 'claude-md') {
222
+ return path.join(cwd, '.claude', 'CLAUDE.md');
223
+ }
224
+ if (artifactType === 'settings') {
225
+ return path.join(cwd, '.claude', 'settings.json');
226
+ }
227
+
228
+ const type = ARTIFACT_TYPES[artifactType];
229
+ if (!type) throw new Error(`Unknown artifact type: ${artifactType}`);
230
+ return path.join(cwd, '.claude', type.sourceSubdir);
231
+ }
232
+
216
233
  // Backward-compatible aliases
217
234
  function getLocalSkillsDir(root) {
218
235
  return getSourceArtifactDir('local', 'skills', root);
@@ -257,6 +274,7 @@ module.exports = {
257
274
  getSyncTargetDir,
258
275
  getSyncTempDir,
259
276
  getInstallTarget,
277
+ getScopedInstallTarget,
260
278
  USER_DATA_DIR,
261
279
  // Backward-compatible
262
280
  getLocalSkillsDir,
@@ -0,0 +1,92 @@
1
+ const fs = require('fs');
2
+ const { getProjectRoot, getSourceArtifactDir } = require('../config/paths');
3
+ const { readConfig, filterItemsByAllowlist } = require('../config/sources');
4
+ const { loadSkillsFromSource } = require('./skill-merger');
5
+ const { loadAgentsFromSource } = require('./agent-merger');
6
+ const { loadCommandsFromSource } = require('./command-merger');
7
+ const { loadHookFilesFromSource } = require('./hook-merger');
8
+ const { loadFilesFromSource } = require('./file-merger');
9
+ const { loadClaudeMd } = require('./claude-md-merger');
10
+
11
+ function loadSectionDocumentFromSource(sourceDir) {
12
+ const content = loadClaudeMd(sourceDir);
13
+ if (!content) return [];
14
+ return [{
15
+ name: 'CLAUDE.md',
16
+ path: sourceDir,
17
+ metadata: {
18
+ description: `${content.length} chars of prompt guidelines`,
19
+ },
20
+ }];
21
+ }
22
+
23
+ function getArtifactLoader(artifactType, options = {}) {
24
+ switch (artifactType) {
25
+ case 'skills': return loadSkillsFromSource;
26
+ case 'agents': return loadAgentsFromSource;
27
+ case 'commands': return loadCommandsFromSource;
28
+ case 'hooks': return loadHookFilesFromSource;
29
+ case 'hud': return loadFilesFromSource;
30
+ case 'guidelines':
31
+ case 'claude-md':
32
+ return options.includeSectionDocuments ? loadSectionDocumentFromSource : null;
33
+ default: return null;
34
+ }
35
+ }
36
+
37
+ function getOrderedInstallableSources(config) {
38
+ return Object.entries(config.sources || {})
39
+ .sort(([, a], [, b]) => a.priority - b.priority)
40
+ .filter(([, src]) => src.role !== 'reference')
41
+ .filter(([, src]) => !src.installMode || src.installMode === 'auto');
42
+ }
43
+
44
+ function collectSourceDirsForType(artifactType, root = getProjectRoot(), config = readConfig()) {
45
+ const sourcesForType = [];
46
+
47
+ for (const [name, src] of getOrderedInstallableSources(config)) {
48
+ if (!(src.artifacts || []).includes(artifactType)) continue;
49
+
50
+ const dir = getSourceArtifactDir(name, artifactType, root);
51
+ if (fs.existsSync(dir)) {
52
+ sourcesForType.push({ name, dir, priority: src.priority, config: src });
53
+ }
54
+ }
55
+
56
+ if (sourcesForType.length === 0 && artifactType === 'claude-md') {
57
+ return collectSourceDirsForType('guidelines', root, config);
58
+ }
59
+
60
+ return sourcesForType;
61
+ }
62
+
63
+ function loadSourcesForType(artifactType, root = getProjectRoot(), options = {}) {
64
+ const config = options.config || readConfig();
65
+ const loader = getArtifactLoader(artifactType, { includeSectionDocuments: true });
66
+ if (!loader) return [];
67
+
68
+ const sources = [];
69
+ for (const source of collectSourceDirsForType(artifactType, root, config)) {
70
+ const items = filterItemsByAllowlist(
71
+ source.config,
72
+ artifactType,
73
+ loader(source.dir, source.name),
74
+ );
75
+ if (items.length > 0) {
76
+ sources.push({ name: source.name, items });
77
+ }
78
+ }
79
+
80
+ if (sources.length === 0 && artifactType === 'claude-md') {
81
+ return loadSourcesForType('guidelines', root, options);
82
+ }
83
+
84
+ return sources;
85
+ }
86
+
87
+ module.exports = {
88
+ collectSourceDirsForType,
89
+ getArtifactLoader,
90
+ loadSectionDocumentFromSource,
91
+ loadSourcesForType,
92
+ };