@slats/claude-assets-sync 0.0.3 → 0.0.5

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.
Files changed (52) hide show
  1. package/dist/commands/add.cjs +2 -7
  2. package/dist/commands/add.mjs +2 -7
  3. package/dist/commands/index.d.ts +22 -0
  4. package/dist/commands/remove.cjs +13 -13
  5. package/dist/commands/remove.mjs +7 -7
  6. package/dist/commands/types.d.ts +2 -4
  7. package/dist/commands/update.cjs +178 -0
  8. package/dist/commands/update.d.ts +13 -0
  9. package/dist/commands/update.mjs +176 -0
  10. package/dist/components/add/AddCommand.cjs +11 -0
  11. package/dist/components/add/AddCommand.mjs +11 -0
  12. package/dist/components/list/ListCommand.cjs +65 -47
  13. package/dist/components/list/ListCommand.mjs +66 -48
  14. package/dist/components/status/StatusDisplay.cjs +4 -3
  15. package/dist/components/status/StatusDisplay.d.ts +2 -4
  16. package/dist/components/status/StatusDisplay.mjs +4 -3
  17. package/dist/components/tree/AssetTreeNode.cjs +21 -4
  18. package/dist/components/tree/AssetTreeNode.d.ts +1 -1
  19. package/dist/components/tree/AssetTreeNode.mjs +21 -4
  20. package/dist/components/tree/TreeSelect.cjs +14 -6
  21. package/dist/components/tree/TreeSelect.mjs +14 -6
  22. package/dist/core/assetStructure.d.ts +1 -1
  23. package/dist/core/cli.cjs +18 -0
  24. package/dist/core/cli.mjs +18 -0
  25. package/dist/core/constants.cjs +4 -1
  26. package/dist/core/constants.d.ts +8 -1
  27. package/dist/core/constants.mjs +4 -1
  28. package/dist/core/filesystem.cjs +18 -12
  29. package/dist/core/filesystem.d.ts +3 -2
  30. package/dist/core/filesystem.mjs +18 -12
  31. package/dist/core/github.cjs +31 -3
  32. package/dist/core/github.d.ts +14 -1
  33. package/dist/core/github.mjs +31 -4
  34. package/dist/core/migration.cjs +17 -15
  35. package/dist/core/migration.mjs +12 -10
  36. package/dist/core/packageScanner.cjs +110 -29
  37. package/dist/core/packageScanner.d.ts +6 -1
  38. package/dist/core/packageScanner.mjs +109 -30
  39. package/dist/core/sync.cjs +83 -59
  40. package/dist/core/sync.mjs +55 -31
  41. package/dist/core/syncMeta.cjs +153 -9
  42. package/dist/core/syncMeta.d.ts +22 -18
  43. package/dist/core/syncMeta.mjs +149 -9
  44. package/dist/utils/nameTransform.cjs +7 -6
  45. package/dist/utils/nameTransform.d.ts +22 -33
  46. package/dist/utils/nameTransform.mjs +8 -6
  47. package/dist/utils/types.d.ts +24 -7
  48. package/dist/version.cjs +1 -1
  49. package/dist/version.d.ts +1 -1
  50. package/dist/version.mjs +1 -1
  51. package/package.json +2 -2
  52. package/CHANGELOG.md +0 -189
@@ -38,13 +38,8 @@ async function performSync(selection, cwd) {
38
38
  files: [],
39
39
  };
40
40
  for (const assetType of Object.keys(selection.excludedAssets)) {
41
- for (const path of selection.excludedAssets[assetType]) {
42
- if (path.includes('.')) {
43
- exclusions.files.push(path);
44
- }
45
- else {
46
- exclusions.directories.push(path);
47
- }
41
+ for (const excludedPath of selection.excludedAssets[assetType]) {
42
+ exclusions.files.push(excludedPath);
48
43
  }
49
44
  }
50
45
  await sync.syncPackage(selection.packageName, {
@@ -36,13 +36,8 @@ async function performSync(selection, cwd) {
36
36
  files: [],
37
37
  };
38
38
  for (const assetType of Object.keys(selection.excludedAssets)) {
39
- for (const path of selection.excludedAssets[assetType]) {
40
- if (path.includes('.')) {
41
- exclusions.files.push(path);
42
- }
43
- else {
44
- exclusions.directories.push(path);
45
- }
39
+ for (const excludedPath of selection.excludedAssets[assetType]) {
40
+ exclusions.files.push(excludedPath);
46
41
  }
47
42
  }
48
43
  await syncPackage(selection.packageName, {
@@ -8,7 +8,9 @@ export { runRemoveCommand } from './remove';
8
8
  export { runStatusCommand } from './status';
9
9
  export { runMigrateCommand } from './migrate';
10
10
  export { runAddCommand } from './add';
11
+ export { runUpdateCommand } from './update';
11
12
  export type { AddCommandOptions } from './add';
13
+ export type { UpdateCommandOptions } from './update';
12
14
  /**
13
15
  * Command metadata for CLI help and documentation
14
16
  */
@@ -88,4 +90,24 @@ export declare const COMMANDS: {
88
90
  readonly description: "Preview migration without making changes";
89
91
  }];
90
92
  };
93
+ readonly update: {
94
+ readonly name: "update";
95
+ readonly description: "Update package metadata in .sync-meta.json";
96
+ readonly options: readonly [{
97
+ readonly flag: "-p, --package <name>";
98
+ readonly description: "Package name to update (default: all)";
99
+ }, {
100
+ readonly flag: "-l, --local";
101
+ readonly description: "Read from local workspace";
102
+ }, {
103
+ readonly flag: "-r, --ref <ref>";
104
+ readonly description: "Git ref to fetch from";
105
+ }, {
106
+ readonly flag: "--dry-run";
107
+ readonly description: "Preview changes without writing";
108
+ }, {
109
+ readonly flag: "--sync";
110
+ readonly description: "Re-sync files after updating metadata";
111
+ }];
112
+ };
91
113
  };
@@ -6,7 +6,7 @@ var readline = require('node:readline/promises');
6
6
  var pc = require('picocolors');
7
7
  var syncMeta = require('../core/syncMeta.cjs');
8
8
  var logger = require('../utils/logger.cjs');
9
- var nameTransform = require('../utils/nameTransform.cjs');
9
+ var packageName = require('../utils/packageName.cjs');
10
10
 
11
11
  function _interopNamespaceDefault(e) {
12
12
  var n = Object.create(null);
@@ -30,17 +30,17 @@ var path__namespace = /*#__PURE__*/_interopNamespaceDefault(path);
30
30
  var readline__namespace = /*#__PURE__*/_interopNamespaceDefault(readline);
31
31
 
32
32
  const runRemoveCommand = async (options, cwd = process.cwd()) => {
33
- const { package: packageName, yes, dryRun } = options;
34
- const prefix = nameTransform.packageNameToPrefix(packageName);
33
+ const { package: packageName$1, yes, dryRun } = options;
34
+ const prefix = packageName.packageNameToPrefix(packageName$1);
35
35
  const meta = syncMeta.readUnifiedSyncMeta(cwd);
36
36
  if (!meta || !meta.packages || !meta.packages[prefix]) {
37
- logger.logger.error(`Package ${packageName} is not synced.`);
37
+ logger.logger.error(`Package ${packageName$1} is not synced.`);
38
38
  process.exit(1);
39
39
  return;
40
40
  }
41
41
  const packageInfo = meta.packages[prefix];
42
42
  if (!packageInfo || !packageInfo.files) {
43
- logger.logger.error(`Package ${packageName} has no files to remove.`);
43
+ logger.logger.error(`Package ${packageName$1} has no files to remove.`);
44
44
  process.exit(1);
45
45
  return;
46
46
  }
@@ -48,16 +48,16 @@ const runRemoveCommand = async (options, cwd = process.cwd()) => {
48
48
  for (const [assetType, files] of Object.entries(packageInfo.files)) {
49
49
  if (!Array.isArray(files) || files.length === 0)
50
50
  continue;
51
- const firstFile = files[0];
52
- const isFlat = typeof firstFile === 'object' && 'transformed' in firstFile;
53
- if (isFlat) {
54
- for (const file of files) {
55
- const filePath = path__namespace.join(cwd, '.claude', assetType, file.transformed);
56
- filesToRemove.push({ assetType, path: filePath });
51
+ const units = files;
52
+ const firstUnit = units[0];
53
+ if (firstUnit.transformed) {
54
+ for (const unit of units) {
55
+ const targetPath = path__namespace.join(cwd, '.claude', assetType, unit.transformed);
56
+ filesToRemove.push({ assetType, path: targetPath });
57
57
  }
58
58
  }
59
59
  else {
60
- const dirPath = path__namespace.join(cwd, '.claude', assetType, packageName);
60
+ const dirPath = path__namespace.join(cwd, '.claude', assetType, packageName$1);
61
61
  filesToRemove.push({ assetType, path: dirPath });
62
62
  }
63
63
  }
@@ -103,7 +103,7 @@ const runRemoveCommand = async (options, cwd = process.cwd()) => {
103
103
  delete meta.packages[prefix];
104
104
  meta.syncedAt = new Date().toISOString();
105
105
  syncMeta.writeUnifiedSyncMeta(cwd, meta);
106
- logger.logger.success(`\nRemoved package ${packageName}`);
106
+ logger.logger.success(`\nRemoved package ${packageName$1}`);
107
107
  };
108
108
 
109
109
  exports.runRemoveCommand = runRemoveCommand;
@@ -4,7 +4,7 @@ import * as readline from 'node:readline/promises';
4
4
  import pc from 'picocolors';
5
5
  import { readUnifiedSyncMeta, writeUnifiedSyncMeta } from '../core/syncMeta.mjs';
6
6
  import { logger } from '../utils/logger.mjs';
7
- import { packageNameToPrefix } from '../utils/nameTransform.mjs';
7
+ import { packageNameToPrefix } from '../utils/packageName.mjs';
8
8
 
9
9
  const runRemoveCommand = async (options, cwd = process.cwd()) => {
10
10
  const { package: packageName, yes, dryRun } = options;
@@ -25,12 +25,12 @@ const runRemoveCommand = async (options, cwd = process.cwd()) => {
25
25
  for (const [assetType, files] of Object.entries(packageInfo.files)) {
26
26
  if (!Array.isArray(files) || files.length === 0)
27
27
  continue;
28
- const firstFile = files[0];
29
- const isFlat = typeof firstFile === 'object' && 'transformed' in firstFile;
30
- if (isFlat) {
31
- for (const file of files) {
32
- const filePath = path.join(cwd, '.claude', assetType, file.transformed);
33
- filesToRemove.push({ assetType, path: filePath });
28
+ const units = files;
29
+ const firstUnit = units[0];
30
+ if (firstUnit.transformed) {
31
+ for (const unit of units) {
32
+ const targetPath = path.join(cwd, '.claude', assetType, unit.transformed);
33
+ filesToRemove.push({ assetType, path: targetPath });
34
34
  }
35
35
  }
36
36
  else {
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Common types for command modules
3
3
  */
4
+ import type { SkillUnit } from '../utils/types';
4
5
  /**
5
6
  * Base command result
6
7
  */
@@ -65,10 +66,7 @@ export interface PackageStatusItem {
65
66
  upToDate: boolean;
66
67
  syncedAt: string;
67
68
  error?: string;
68
- files: Record<string, Array<string | {
69
- original: string;
70
- transformed: string;
71
- }>>;
69
+ files: Record<string, SkillUnit[]>;
72
70
  fileCount: number;
73
71
  }
74
72
  /**
@@ -0,0 +1,178 @@
1
+ 'use strict';
2
+
3
+ var pc = require('picocolors');
4
+ var packageScanner = require('../core/packageScanner.cjs');
5
+ var sync = require('../core/sync.cjs');
6
+ var syncMeta = require('../core/syncMeta.cjs');
7
+ var constants = require('../core/constants.cjs');
8
+ var logger = require('../utils/logger.cjs');
9
+ var _package = require('../utils/package.cjs');
10
+ var packageName = require('../utils/packageName.cjs');
11
+
12
+ const runUpdateCommand = async (options, cwd = process.cwd()) => {
13
+ const destDir = _package.findGitRoot(cwd) ?? cwd;
14
+ const meta = syncMeta.readUnifiedSyncMeta(destDir);
15
+ if (!meta || Object.keys(meta.packages).length === 0) {
16
+ logger.logger.info('No packages synced yet. Use "sync" or "add" first.');
17
+ return;
18
+ }
19
+ const packagesToUpdate = [];
20
+ if (options.package) {
21
+ const prefix = packageName.packageNameToPrefix(options.package);
22
+ if (!meta.packages[prefix]) {
23
+ logger.logger.error(`Package ${options.package} is not synced.`);
24
+ process.exit(1);
25
+ return;
26
+ }
27
+ packagesToUpdate.push(prefix);
28
+ }
29
+ else {
30
+ packagesToUpdate.push(...Object.keys(meta.packages));
31
+ }
32
+ let updatedMeta = { ...meta };
33
+ let hasChanges = false;
34
+ for (const prefix of packagesToUpdate) {
35
+ const packageInfo = updatedMeta.packages[prefix];
36
+ const packageName = packageInfo.originalName;
37
+ console.log(pc.cyan(`\nUpdating ${packageName}...`));
38
+ const isLocal = options.local ?? packageInfo.local ?? false;
39
+ const currentPkgInfo = isLocal
40
+ ? _package.readLocalPackageJson(packageName, cwd)
41
+ : _package.readPackageJson(packageName, cwd);
42
+ if (!currentPkgInfo) {
43
+ const location = isLocal ? 'workspace' : 'node_modules';
44
+ console.log(pc.yellow(` ⚠ Package not found in ${location}, skipping version update`));
45
+ }
46
+ else {
47
+ const newVersion = currentPkgInfo.version;
48
+ if (newVersion !== packageInfo.version) {
49
+ if (options.dryRun) {
50
+ console.log(pc.gray(` [DRY RUN] Would update version: ${packageInfo.version} → ${newVersion}`));
51
+ }
52
+ else {
53
+ updatedMeta = syncMeta.updatePackageVersion(updatedMeta, prefix, newVersion);
54
+ console.log(pc.green(` ✓ Version updated: ${packageInfo.version} → ${newVersion}`));
55
+ hasChanges = true;
56
+ }
57
+ }
58
+ else {
59
+ console.log(pc.gray(` Version unchanged: ${packageInfo.version}`));
60
+ }
61
+ }
62
+ try {
63
+ const scannedTrees = await packageScanner.scanPackageAssets(packageName, {
64
+ local: isLocal,
65
+ ref: options.ref,
66
+ cwd,
67
+ });
68
+ if (scannedTrees && scannedTrees.length > 0) {
69
+ for (const assetTypeTree of scannedTrees) {
70
+ const assetType = assetTypeTree.label;
71
+ const scannedUnits = packageScanner.buildSkillUnitsFromTree(assetTypeTree, prefix);
72
+ const currentUnits = packageInfo.files[assetType] || [];
73
+ const scannedNames = new Set(scannedUnits.map((u) => u.name));
74
+ const currentNames = new Set(currentUnits.map((u) => u.name));
75
+ const added = scannedUnits.filter((u) => !currentNames.has(u.name));
76
+ const removed = currentUnits.filter((u) => !scannedNames.has(u.name));
77
+ const unchanged = currentUnits.filter((u) => scannedNames.has(u.name));
78
+ if (added.length > 0 || removed.length > 0) {
79
+ const mergedUnits = [...unchanged, ...added];
80
+ if (options.dryRun) {
81
+ if (added.length > 0) {
82
+ console.log(pc.gray(` [DRY RUN] ${assetType}: ${added.length} new skill(s) found`));
83
+ for (const u of added) {
84
+ console.log(pc.gray(` + ${u.name}${u.isDirectory ? '/' : ''}`));
85
+ }
86
+ }
87
+ if (removed.length > 0) {
88
+ console.log(pc.yellow(` [DRY RUN] ${assetType}: ${removed.length} skill(s) no longer in upstream`));
89
+ for (const u of removed) {
90
+ console.log(pc.yellow(` - ${u.name}${u.isDirectory ? '/' : ''}`));
91
+ }
92
+ }
93
+ }
94
+ else {
95
+ updatedMeta = syncMeta.updatePackageFilesystemMeta(updatedMeta, prefix, assetType, mergedUnits);
96
+ if (added.length > 0) {
97
+ console.log(pc.green(` ✓ ${assetType}: ${added.length} new skill(s) added to meta`));
98
+ for (const u of added) {
99
+ console.log(pc.green(` + ${u.name}${u.isDirectory ? '/' : ''}`));
100
+ }
101
+ }
102
+ if (removed.length > 0) {
103
+ console.log(pc.yellow(` ⚠ ${assetType}: ${removed.length} skill(s) no longer in upstream (kept in meta)`));
104
+ for (const u of removed) {
105
+ console.log(pc.yellow(` - ${u.name}${u.isDirectory ? '/' : ''}`));
106
+ }
107
+ }
108
+ hasChanges = true;
109
+ }
110
+ }
111
+ else {
112
+ let internalChanged = false;
113
+ const refreshedUnits = currentUnits.map((current) => {
114
+ const scanned = scannedUnits.find((s) => s.name === current.name);
115
+ if (scanned && scanned.isDirectory && current.isDirectory) {
116
+ const currentInternal = (current.internalFiles || []).sort().join(',');
117
+ const scannedInternal = (scanned.internalFiles || []).sort().join(',');
118
+ if (currentInternal !== scannedInternal) {
119
+ internalChanged = true;
120
+ return { ...current, internalFiles: scanned.internalFiles };
121
+ }
122
+ }
123
+ return current;
124
+ });
125
+ if (internalChanged && !options.dryRun) {
126
+ updatedMeta = syncMeta.updatePackageFilesystemMeta(updatedMeta, prefix, assetType, refreshedUnits);
127
+ console.log(pc.green(` ✓ ${assetType}: internal file lists updated`));
128
+ hasChanges = true;
129
+ }
130
+ else if (internalChanged && options.dryRun) {
131
+ console.log(pc.gray(` [DRY RUN] ${assetType}: would update internal file lists`));
132
+ }
133
+ else {
134
+ console.log(pc.gray(` ${assetType}: no changes`));
135
+ }
136
+ }
137
+ }
138
+ }
139
+ }
140
+ catch {
141
+ console.log(pc.yellow(` ⚠ Could not scan package assets, skipping filesystem update`));
142
+ }
143
+ if (options.sync && !options.dryRun) {
144
+ console.log(pc.cyan(` Syncing files...`));
145
+ try {
146
+ await sync.syncPackage(packageName, {
147
+ force: true,
148
+ dryRun: false,
149
+ local: isLocal,
150
+ ref: options.ref,
151
+ flat: true,
152
+ }, cwd, packageInfo.exclusions, destDir);
153
+ console.log(pc.green(` ✓ Files synced`));
154
+ }
155
+ catch (error) {
156
+ const message = error instanceof Error ? error.message : 'Unknown error';
157
+ console.log(pc.red(` ✗ Sync failed: ${message}`));
158
+ }
159
+ }
160
+ else if (options.sync && options.dryRun) {
161
+ console.log(pc.gray(` [DRY RUN] Would re-sync files`));
162
+ }
163
+ }
164
+ if (hasChanges && !options.dryRun) {
165
+ updatedMeta.syncedAt = new Date().toISOString();
166
+ updatedMeta.skillUnitFormat = constants.SCHEMA_VERSIONS.SKILL_UNIT_FORMAT;
167
+ syncMeta.writeUnifiedSyncMeta(destDir, updatedMeta);
168
+ console.log(pc.green(`\n✓ Metadata updated`));
169
+ }
170
+ else if (options.dryRun) {
171
+ console.log(pc.gray(`\n[DRY RUN] No changes written`));
172
+ }
173
+ else {
174
+ console.log(pc.gray(`\nNo changes needed`));
175
+ }
176
+ };
177
+
178
+ exports.runUpdateCommand = runUpdateCommand;
@@ -0,0 +1,13 @@
1
+ export interface UpdateCommandOptions {
2
+ package?: string;
3
+ local?: boolean;
4
+ ref?: string;
5
+ dryRun?: boolean;
6
+ sync?: boolean;
7
+ }
8
+ /**
9
+ * Run the update command
10
+ * @param options - Update command options
11
+ * @param cwd - Current working directory
12
+ */
13
+ export declare const runUpdateCommand: (options: UpdateCommandOptions, cwd?: string) => Promise<void>;
@@ -0,0 +1,176 @@
1
+ import pc from 'picocolors';
2
+ import { scanPackageAssets, buildSkillUnitsFromTree } from '../core/packageScanner.mjs';
3
+ import { syncPackage } from '../core/sync.mjs';
4
+ import { readUnifiedSyncMeta, updatePackageVersion, updatePackageFilesystemMeta, writeUnifiedSyncMeta } from '../core/syncMeta.mjs';
5
+ import { SCHEMA_VERSIONS } from '../core/constants.mjs';
6
+ import { logger } from '../utils/logger.mjs';
7
+ import { findGitRoot, readLocalPackageJson, readPackageJson } from '../utils/package.mjs';
8
+ import { packageNameToPrefix } from '../utils/packageName.mjs';
9
+
10
+ const runUpdateCommand = async (options, cwd = process.cwd()) => {
11
+ const destDir = findGitRoot(cwd) ?? cwd;
12
+ const meta = readUnifiedSyncMeta(destDir);
13
+ if (!meta || Object.keys(meta.packages).length === 0) {
14
+ logger.info('No packages synced yet. Use "sync" or "add" first.');
15
+ return;
16
+ }
17
+ const packagesToUpdate = [];
18
+ if (options.package) {
19
+ const prefix = packageNameToPrefix(options.package);
20
+ if (!meta.packages[prefix]) {
21
+ logger.error(`Package ${options.package} is not synced.`);
22
+ process.exit(1);
23
+ return;
24
+ }
25
+ packagesToUpdate.push(prefix);
26
+ }
27
+ else {
28
+ packagesToUpdate.push(...Object.keys(meta.packages));
29
+ }
30
+ let updatedMeta = { ...meta };
31
+ let hasChanges = false;
32
+ for (const prefix of packagesToUpdate) {
33
+ const packageInfo = updatedMeta.packages[prefix];
34
+ const packageName = packageInfo.originalName;
35
+ console.log(pc.cyan(`\nUpdating ${packageName}...`));
36
+ const isLocal = options.local ?? packageInfo.local ?? false;
37
+ const currentPkgInfo = isLocal
38
+ ? readLocalPackageJson(packageName, cwd)
39
+ : readPackageJson(packageName, cwd);
40
+ if (!currentPkgInfo) {
41
+ const location = isLocal ? 'workspace' : 'node_modules';
42
+ console.log(pc.yellow(` ⚠ Package not found in ${location}, skipping version update`));
43
+ }
44
+ else {
45
+ const newVersion = currentPkgInfo.version;
46
+ if (newVersion !== packageInfo.version) {
47
+ if (options.dryRun) {
48
+ console.log(pc.gray(` [DRY RUN] Would update version: ${packageInfo.version} → ${newVersion}`));
49
+ }
50
+ else {
51
+ updatedMeta = updatePackageVersion(updatedMeta, prefix, newVersion);
52
+ console.log(pc.green(` ✓ Version updated: ${packageInfo.version} → ${newVersion}`));
53
+ hasChanges = true;
54
+ }
55
+ }
56
+ else {
57
+ console.log(pc.gray(` Version unchanged: ${packageInfo.version}`));
58
+ }
59
+ }
60
+ try {
61
+ const scannedTrees = await scanPackageAssets(packageName, {
62
+ local: isLocal,
63
+ ref: options.ref,
64
+ cwd,
65
+ });
66
+ if (scannedTrees && scannedTrees.length > 0) {
67
+ for (const assetTypeTree of scannedTrees) {
68
+ const assetType = assetTypeTree.label;
69
+ const scannedUnits = buildSkillUnitsFromTree(assetTypeTree, prefix);
70
+ const currentUnits = packageInfo.files[assetType] || [];
71
+ const scannedNames = new Set(scannedUnits.map((u) => u.name));
72
+ const currentNames = new Set(currentUnits.map((u) => u.name));
73
+ const added = scannedUnits.filter((u) => !currentNames.has(u.name));
74
+ const removed = currentUnits.filter((u) => !scannedNames.has(u.name));
75
+ const unchanged = currentUnits.filter((u) => scannedNames.has(u.name));
76
+ if (added.length > 0 || removed.length > 0) {
77
+ const mergedUnits = [...unchanged, ...added];
78
+ if (options.dryRun) {
79
+ if (added.length > 0) {
80
+ console.log(pc.gray(` [DRY RUN] ${assetType}: ${added.length} new skill(s) found`));
81
+ for (const u of added) {
82
+ console.log(pc.gray(` + ${u.name}${u.isDirectory ? '/' : ''}`));
83
+ }
84
+ }
85
+ if (removed.length > 0) {
86
+ console.log(pc.yellow(` [DRY RUN] ${assetType}: ${removed.length} skill(s) no longer in upstream`));
87
+ for (const u of removed) {
88
+ console.log(pc.yellow(` - ${u.name}${u.isDirectory ? '/' : ''}`));
89
+ }
90
+ }
91
+ }
92
+ else {
93
+ updatedMeta = updatePackageFilesystemMeta(updatedMeta, prefix, assetType, mergedUnits);
94
+ if (added.length > 0) {
95
+ console.log(pc.green(` ✓ ${assetType}: ${added.length} new skill(s) added to meta`));
96
+ for (const u of added) {
97
+ console.log(pc.green(` + ${u.name}${u.isDirectory ? '/' : ''}`));
98
+ }
99
+ }
100
+ if (removed.length > 0) {
101
+ console.log(pc.yellow(` ⚠ ${assetType}: ${removed.length} skill(s) no longer in upstream (kept in meta)`));
102
+ for (const u of removed) {
103
+ console.log(pc.yellow(` - ${u.name}${u.isDirectory ? '/' : ''}`));
104
+ }
105
+ }
106
+ hasChanges = true;
107
+ }
108
+ }
109
+ else {
110
+ let internalChanged = false;
111
+ const refreshedUnits = currentUnits.map((current) => {
112
+ const scanned = scannedUnits.find((s) => s.name === current.name);
113
+ if (scanned && scanned.isDirectory && current.isDirectory) {
114
+ const currentInternal = (current.internalFiles || []).sort().join(',');
115
+ const scannedInternal = (scanned.internalFiles || []).sort().join(',');
116
+ if (currentInternal !== scannedInternal) {
117
+ internalChanged = true;
118
+ return { ...current, internalFiles: scanned.internalFiles };
119
+ }
120
+ }
121
+ return current;
122
+ });
123
+ if (internalChanged && !options.dryRun) {
124
+ updatedMeta = updatePackageFilesystemMeta(updatedMeta, prefix, assetType, refreshedUnits);
125
+ console.log(pc.green(` ✓ ${assetType}: internal file lists updated`));
126
+ hasChanges = true;
127
+ }
128
+ else if (internalChanged && options.dryRun) {
129
+ console.log(pc.gray(` [DRY RUN] ${assetType}: would update internal file lists`));
130
+ }
131
+ else {
132
+ console.log(pc.gray(` ${assetType}: no changes`));
133
+ }
134
+ }
135
+ }
136
+ }
137
+ }
138
+ catch {
139
+ console.log(pc.yellow(` ⚠ Could not scan package assets, skipping filesystem update`));
140
+ }
141
+ if (options.sync && !options.dryRun) {
142
+ console.log(pc.cyan(` Syncing files...`));
143
+ try {
144
+ await syncPackage(packageName, {
145
+ force: true,
146
+ dryRun: false,
147
+ local: isLocal,
148
+ ref: options.ref,
149
+ flat: true,
150
+ }, cwd, packageInfo.exclusions, destDir);
151
+ console.log(pc.green(` ✓ Files synced`));
152
+ }
153
+ catch (error) {
154
+ const message = error instanceof Error ? error.message : 'Unknown error';
155
+ console.log(pc.red(` ✗ Sync failed: ${message}`));
156
+ }
157
+ }
158
+ else if (options.sync && options.dryRun) {
159
+ console.log(pc.gray(` [DRY RUN] Would re-sync files`));
160
+ }
161
+ }
162
+ if (hasChanges && !options.dryRun) {
163
+ updatedMeta.syncedAt = new Date().toISOString();
164
+ updatedMeta.skillUnitFormat = SCHEMA_VERSIONS.SKILL_UNIT_FORMAT;
165
+ writeUnifiedSyncMeta(destDir, updatedMeta);
166
+ console.log(pc.green(`\n✓ Metadata updated`));
167
+ }
168
+ else if (options.dryRun) {
169
+ console.log(pc.gray(`\n[DRY RUN] No changes written`));
170
+ }
171
+ else {
172
+ console.log(pc.gray(`\nNo changes needed`));
173
+ }
174
+ };
175
+
176
+ export { runUpdateCommand };
@@ -70,6 +70,17 @@ const AddCommand = ({ packageName, local, ref, onComplete, onError, onCancel, })
70
70
  };
71
71
  function extractSelection(nodes, included, excluded) {
72
72
  for (const node of nodes) {
73
+ if (node.disabled)
74
+ continue;
75
+ if (node.type === 'skill-directory') {
76
+ if (node.selected) {
77
+ included.push(node.path);
78
+ }
79
+ else {
80
+ excluded.push(node.path);
81
+ }
82
+ continue;
83
+ }
73
84
  if (node.selected) {
74
85
  included.push(node.path);
75
86
  }
@@ -68,6 +68,17 @@ const AddCommand = ({ packageName, local, ref, onComplete, onError, onCancel, })
68
68
  };
69
69
  function extractSelection(nodes, included, excluded) {
70
70
  for (const node of nodes) {
71
+ if (node.disabled)
72
+ continue;
73
+ if (node.type === 'skill-directory') {
74
+ if (node.selected) {
75
+ included.push(node.path);
76
+ }
77
+ else {
78
+ excluded.push(node.path);
79
+ }
80
+ continue;
81
+ }
71
82
  if (node.selected) {
72
83
  included.push(node.path);
73
84
  }