@open-agent-toolkit/cli 0.1.74 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (77) hide show
  1. package/assets/docs/cli-utilities/configuration.md +18 -12
  2. package/assets/docs/cli-utilities/tool-packs.md +1 -1
  3. package/assets/docs/provider-sync/commands.md +26 -0
  4. package/assets/docs/provider-sync/config.md +17 -6
  5. package/assets/docs/provider-sync/index.md +17 -5
  6. package/assets/docs/provider-sync/manifest-and-drift.md +39 -1
  7. package/assets/docs/provider-sync/providers.md +8 -2
  8. package/assets/docs/provider-sync/scope-and-surface.md +5 -1
  9. package/assets/docs/reference/file-locations.md +9 -3
  10. package/assets/docs/reference/oat-directory-structure.md +11 -3
  11. package/assets/docs/reference/troubleshooting.md +11 -0
  12. package/assets/docs/workflows/index.md +1 -0
  13. package/assets/docs/workflows/skills/index.md +3 -0
  14. package/assets/docs/workflows/wave-workflows.md +77 -0
  15. package/assets/public-package-versions.json +4 -4
  16. package/assets/skills/create-agnostic-skill/SKILL.md +1 -1
  17. package/assets/skills/create-agnostic-skill/references/docs/skills-guide.md +24 -15
  18. package/assets/skills/oat-agent-instructions-analyze/references/docs/provider-reference.md +9 -3
  19. package/assets/skills/oat-agent-instructions-apply/references/docs/provider-reference.md +9 -3
  20. package/assets/skills/oat-wave-execute/SKILL.md +317 -0
  21. package/assets/skills/oat-wave-execute/assets/orchestration-log-template.md +54 -0
  22. package/assets/skills/oat-wave-execute/assets/wrapper-plan-template.md +157 -0
  23. package/assets/skills/oat-wave-execute/scripts/bootstrap-group.sh +195 -0
  24. package/assets/skills/oat-wave-program/SKILL.md +109 -0
  25. package/assets/skills/oat-wave-program/assets/execution-program-template.md +40 -0
  26. package/dist/commands/config/index.d.ts.map +1 -1
  27. package/dist/commands/config/index.js +55 -0
  28. package/dist/commands/init/index.d.ts +6 -2
  29. package/dist/commands/init/index.d.ts.map +1 -1
  30. package/dist/commands/init/index.js +90 -31
  31. package/dist/commands/init/tools/shared/skill-manifest.d.ts +1 -1
  32. package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
  33. package/dist/commands/init/tools/shared/skill-manifest.js +2 -0
  34. package/dist/commands/project/validate-plan/index.d.ts.map +1 -1
  35. package/dist/commands/project/validate-plan/index.js +2 -1
  36. package/dist/commands/project/validate-plan/validate-plan.js +1 -1
  37. package/dist/commands/remove/skill/remove-skill.d.ts +2 -0
  38. package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
  39. package/dist/commands/remove/skill/remove-skill.js +20 -2
  40. package/dist/commands/shared/adopt-stray.d.ts.map +1 -1
  41. package/dist/commands/shared/adopt-stray.js +3 -0
  42. package/dist/commands/shared/cursor-skill-disposition.d.ts +17 -0
  43. package/dist/commands/shared/cursor-skill-disposition.d.ts.map +1 -0
  44. package/dist/commands/shared/cursor-skill-disposition.js +41 -0
  45. package/dist/commands/status/index.d.ts +10 -4
  46. package/dist/commands/status/index.d.ts.map +1 -1
  47. package/dist/commands/status/index.js +99 -31
  48. package/dist/config/oat-config.d.ts +0 -1
  49. package/dist/config/oat-config.d.ts.map +1 -1
  50. package/dist/config/oat-config.js +26 -23
  51. package/dist/config/sync-config.d.ts +6 -2
  52. package/dist/config/sync-config.d.ts.map +1 -1
  53. package/dist/config/sync-config.js +18 -6
  54. package/dist/config/user-sync-config.d.ts +11 -0
  55. package/dist/config/user-sync-config.d.ts.map +1 -0
  56. package/dist/config/user-sync-config.js +61 -0
  57. package/dist/drift/strays.d.ts +1 -1
  58. package/dist/drift/strays.d.ts.map +1 -1
  59. package/dist/drift/strays.js +2 -1
  60. package/dist/engine/compute-plan.d.ts.map +1 -1
  61. package/dist/engine/compute-plan.js +154 -4
  62. package/dist/engine/engine.types.d.ts +2 -2
  63. package/dist/engine/engine.types.d.ts.map +1 -1
  64. package/dist/engine/engine.types.js +1 -0
  65. package/dist/engine/execute-plan.d.ts.map +1 -1
  66. package/dist/engine/execute-plan.js +4 -0
  67. package/dist/fs/io.d.ts.map +1 -1
  68. package/dist/fs/io.js +6 -2
  69. package/dist/manifest/manifest.types.d.ts +12 -12
  70. package/dist/providers/cursor/paths.d.ts.map +1 -1
  71. package/dist/providers/cursor/paths.js +6 -4
  72. package/dist/providers/shared/adapter.types.d.ts +6 -0
  73. package/dist/providers/shared/adapter.types.d.ts.map +1 -1
  74. package/dist/providers/shared/adapter.utils.d.ts +2 -1
  75. package/dist/providers/shared/adapter.utils.d.ts.map +1 -1
  76. package/dist/providers/shared/adapter.utils.js +29 -6
  77. package/package.json +2 -2
@@ -1,8 +1,10 @@
1
- import { access, lstat, readFile, readlink } from 'node:fs/promises';
2
- import { basename, dirname, join, normalize, resolve } from 'node:path';
1
+ import { createHash } from 'node:crypto';
2
+ import { access, lstat, readdir, readFile, readlink, stat, } from 'node:fs/promises';
3
+ import { basename, dirname, join, normalize, relative, resolve, } from 'node:path';
3
4
  import { computeContentHash, computeStringHash } from '../manifest/hash.js';
4
5
  import { findEntry } from '../manifest/manager.js';
5
6
  import { getSyncMappings } from '../providers/shared/adapter.utils.js';
7
+ import { OAT_DIRECTORY_SENTINEL, OAT_MARKER_PREFIX } from './markers.js';
6
8
  function buildUpLevels(depth) {
7
9
  return Array.from({ length: depth }, () => '..');
8
10
  }
@@ -88,6 +90,145 @@ function createRemovalEntry(manifestEntry, scopeRoot) {
88
90
  reason: 'canonical entry no longer exists',
89
91
  };
90
92
  }
93
+ function createRetirementEntry(manifestEntry, scopeRoot, operation, reason) {
94
+ return {
95
+ ...createRemovalEntry(manifestEntry, scopeRoot),
96
+ operation,
97
+ reason,
98
+ };
99
+ }
100
+ function manifestEntryInsideMapping(entry, mapping) {
101
+ if (entry.contentType !== mapping.contentType) {
102
+ return false;
103
+ }
104
+ const canonicalPath = normalize(entry.canonicalPath).replaceAll('\\', '/');
105
+ const canonicalDir = normalize(mapping.canonicalDir).replaceAll('\\', '/');
106
+ return (canonicalPath === canonicalDir ||
107
+ canonicalPath.startsWith(`${canonicalDir}/`));
108
+ }
109
+ async function computeManagedDirectoryCopyHash(providerPath, canonicalPath, contentType) {
110
+ const expectedMarker = `${OAT_MARKER_PREFIX} Source: ${canonicalPath} -->`;
111
+ const sentinelPath = join(providerPath, OAT_DIRECTORY_SENTINEL);
112
+ try {
113
+ const sentinel = await readFile(sentinelPath, 'utf8');
114
+ if (sentinel !== `${expectedMarker}\n`) {
115
+ return null;
116
+ }
117
+ }
118
+ catch {
119
+ return null;
120
+ }
121
+ const files = [];
122
+ async function collectFiles(current) {
123
+ const entries = await readdir(current, { withFileTypes: true });
124
+ for (const entry of entries) {
125
+ const fullPath = join(current, entry.name);
126
+ if (fullPath === sentinelPath) {
127
+ continue;
128
+ }
129
+ if (entry.isDirectory()) {
130
+ if (!(await collectFiles(fullPath))) {
131
+ return false;
132
+ }
133
+ continue;
134
+ }
135
+ if (!entry.isFile()) {
136
+ return false;
137
+ }
138
+ files.push(fullPath);
139
+ }
140
+ return true;
141
+ }
142
+ try {
143
+ if (!(await collectFiles(providerPath))) {
144
+ return null;
145
+ }
146
+ files.sort((left, right) => relative(providerPath, left).localeCompare(relative(providerPath, right)));
147
+ const markerFileName = contentType === 'skill'
148
+ ? 'SKILL.md'
149
+ : contentType === 'agent'
150
+ ? 'AGENT.md'
151
+ : null;
152
+ const markerPath = markerFileName
153
+ ? join(providerPath, markerFileName)
154
+ : null;
155
+ const hash = createHash('sha256');
156
+ for (const file of files) {
157
+ const relativePath = relative(providerPath, file);
158
+ let content = await readFile(file);
159
+ if (file === markerPath) {
160
+ const markerPrefix = Buffer.from(`${expectedMarker}\n`);
161
+ if (content.length < markerPrefix.length ||
162
+ !content.subarray(0, markerPrefix.length).equals(markerPrefix)) {
163
+ return null;
164
+ }
165
+ content = content.subarray(markerPrefix.length);
166
+ }
167
+ hash.update(relativePath);
168
+ hash.update('\0');
169
+ hash.update(content);
170
+ hash.update('\0');
171
+ }
172
+ return hash.digest('hex');
173
+ }
174
+ catch {
175
+ return null;
176
+ }
177
+ }
178
+ async function classifyObsoleteMappingRetirement(manifestEntry, scopeRoot) {
179
+ const providerPath = resolve(scopeRoot, manifestEntry.providerPath);
180
+ const canonicalPath = resolve(scopeRoot, manifestEntry.canonicalPath);
181
+ let providerStat;
182
+ try {
183
+ providerStat = await lstat(providerPath);
184
+ }
185
+ catch (error) {
186
+ if (typeof error === 'object' &&
187
+ error !== null &&
188
+ 'code' in error &&
189
+ error.code === 'ENOENT') {
190
+ return createRetirementEntry(manifestEntry, scopeRoot, 'detach', 'obsolete mapping provider path is missing; detach manifest ownership');
191
+ }
192
+ return createRetirementEntry(manifestEntry, scopeRoot, 'detach', 'obsolete mapping provider path is unverified; preserve and detach manifest ownership');
193
+ }
194
+ if (manifestEntry.strategy === 'symlink') {
195
+ if (providerStat.isSymbolicLink()) {
196
+ try {
197
+ const linkTarget = await readlink(providerPath);
198
+ const resolvedTarget = resolve(dirname(providerPath), linkTarget);
199
+ await stat(resolvedTarget);
200
+ if (resolvedTarget === canonicalPath) {
201
+ return createRetirementEntry(manifestEntry, scopeRoot, 'remove', 'obsolete mapping has verified clean managed symlink');
202
+ }
203
+ }
204
+ catch {
205
+ // Broken or unreadable links are preserved and detached below.
206
+ }
207
+ }
208
+ return createRetirementEntry(manifestEntry, scopeRoot, 'detach', 'obsolete mapping provider path is changed or unverified; preserve and detach manifest ownership');
209
+ }
210
+ const expectedTypeMatches = manifestEntry.isFile
211
+ ? providerStat.isFile()
212
+ : providerStat.isDirectory();
213
+ if (expectedTypeMatches && manifestEntry.contentHash) {
214
+ try {
215
+ const currentHash = await computeContentHash(providerPath, manifestEntry.isFile);
216
+ if (currentHash === manifestEntry.contentHash) {
217
+ return createRetirementEntry(manifestEntry, scopeRoot, 'remove', 'obsolete mapping has verified clean managed copy');
218
+ }
219
+ }
220
+ catch {
221
+ // Hash failures are unverified and therefore non-destructive.
222
+ }
223
+ if (!manifestEntry.isFile) {
224
+ const managedHash = await computeManagedDirectoryCopyHash(providerPath, canonicalPath, manifestEntry.contentType);
225
+ if (managedHash === manifestEntry.contentHash) {
226
+ return createRetirementEntry(manifestEntry, scopeRoot, 'remove', 'obsolete mapping has verified clean managed copy');
227
+ }
228
+ }
229
+ }
230
+ return createRetirementEntry(manifestEntry, scopeRoot, 'detach', 'obsolete mapping provider path is changed or unverified; preserve and detach manifest ownership');
231
+ }
91
232
  async function classifyOperation(canonicalEntry, providerPath, strategy, renderedContent) {
92
233
  if (strategy === 'symlink') {
93
234
  let stat;
@@ -180,16 +321,22 @@ export async function computeSyncPlan({ canonical, adapters, manifest, scope, co
180
321
  const scopeRoot = resolveScopeRoot(canonical, explicitScopeRoot);
181
322
  const seenCanonicalKeys = new Set();
182
323
  const activeProviderNames = new Set();
324
+ const activeMappingsByProvider = new Map();
183
325
  const canonicalFilter = allowedCanonicalPaths
184
326
  ? new Set(allowedCanonicalPaths.map((canonicalPath) => normalize(canonicalPath)))
185
327
  : null;
186
328
  for (const adapter of adapters) {
329
+ if (resolveStrategy(adapter, config)) {
330
+ activeProviderNames.add(adapter.name);
331
+ }
187
332
  for (const mapping of getSyncMappings(adapter, scope)) {
188
333
  const mappingStrategy = resolveStrategy(adapter, config, Boolean(mapping.transformCanonical));
189
334
  if (!mappingStrategy) {
190
335
  continue;
191
336
  }
192
- activeProviderNames.add(adapter.name);
337
+ const activeMappings = activeMappingsByProvider.get(adapter.name) ?? [];
338
+ activeMappings.push(mapping);
339
+ activeMappingsByProvider.set(adapter.name, activeMappings);
193
340
  for (const canonicalEntry of canonical) {
194
341
  if (!entryContentTypeMatches(canonicalEntry, mapping.contentType)) {
195
342
  continue;
@@ -244,7 +391,10 @@ export async function computeSyncPlan({ canonical, adapters, manifest, scope, co
244
391
  !canonicalFilter.has(normalize(manifestEntry.canonicalPath))) {
245
392
  continue;
246
393
  }
247
- removals.push(createRemovalEntry(manifestEntry, scopeRoot));
394
+ const mappingStillExists = (activeMappingsByProvider.get(manifestEntry.provider) ?? []).some((mapping) => manifestEntryInsideMapping(manifestEntry, mapping));
395
+ removals.push(mappingStillExists
396
+ ? createRemovalEntry(manifestEntry, scopeRoot)
397
+ : await classifyObsoleteMappingRetirement(manifestEntry, scopeRoot));
248
398
  }
249
399
  return { scope, entries, removals };
250
400
  }
@@ -1,7 +1,7 @@
1
1
  import type { Scope } from '../shared/types.js';
2
2
  import type { CanonicalEntry } from './scanner.js';
3
3
  export type EngineScope = Exclude<Scope, 'all'>;
4
- export declare const SYNC_OPERATION_TYPES: readonly ["create_symlink", "create_copy", "update_symlink", "update_copy", "remove", "skip"];
4
+ export declare const SYNC_OPERATION_TYPES: readonly ["create_symlink", "create_copy", "update_symlink", "update_copy", "remove", "detach", "skip"];
5
5
  export type SyncOperationType = (typeof SYNC_OPERATION_TYPES)[number];
6
6
  export interface SyncPlanEntry {
7
7
  canonical: CanonicalEntry;
@@ -13,7 +13,7 @@ export interface SyncPlanEntry {
13
13
  renderedContent?: string;
14
14
  }
15
15
  export type RemovalSyncPlanEntry = SyncPlanEntry & {
16
- operation: 'remove';
16
+ operation: 'remove' | 'detach';
17
17
  };
18
18
  export interface SyncPlan {
19
19
  scope: EngineScope;
@@ -1 +1 @@
1
- {"version":3,"file":"engine.types.d.ts","sourceRoot":"","sources":["../../src/engine/engine.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAEhD,eAAO,MAAM,oBAAoB,+FAOvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG;IAAE,SAAS,EAAE,QAAQ,CAAA;CAAE,CAAC;AAE3E,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB"}
1
+ {"version":3,"file":"engine.types.d.ts","sourceRoot":"","sources":["../../src/engine/engine.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAEhD,MAAM,MAAM,WAAW,GAAG,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAEhD,eAAO,MAAM,oBAAoB,yGAQvB,CAAC;AAEX,MAAM,MAAM,iBAAiB,GAAG,CAAC,OAAO,oBAAoB,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,iBAAiB,CAAC;IAC7B,QAAQ,EAAE,SAAS,GAAG,MAAM,CAAC;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,MAAM,oBAAoB,GAAG,aAAa,GAAG;IACjD,SAAS,EAAE,QAAQ,GAAG,QAAQ,CAAC;CAChC,CAAC;AAEF,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,QAAQ,EAAE,oBAAoB,EAAE,CAAC;CAClC;AAED,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB"}
@@ -4,5 +4,6 @@ export const SYNC_OPERATION_TYPES = [
4
4
  'update_symlink',
5
5
  'update_copy',
6
6
  'remove',
7
+ 'detach',
7
8
  'skip',
8
9
  ];
@@ -1 +1 @@
1
- {"version":3,"file":"execute-plan.d.ts","sourceRoot":"","sources":["../../src/engine/execute-plan.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAiB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG1E,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAW5D;AAuJD,wBAAsB,eAAe,CACnC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
1
+ {"version":3,"file":"execute-plan.d.ts","sourceRoot":"","sources":["../../src/engine/execute-plan.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,0BAA0B,CAAC;AAExE,OAAO,KAAK,EAAE,QAAQ,EAAiB,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAG1E,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAW5D;AA2JD,wBAAsB,eAAe,CACnC,IAAI,EAAE,QAAQ,EACd,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,UAAU,CAAC,CA0BrB"}
@@ -98,6 +98,10 @@ async function applyEntry(planEntry, manifest) {
98
98
  const { canonicalPath } = resolveManifestPaths(planEntry);
99
99
  return removeEntry(manifest, canonicalPath, planEntry.provider);
100
100
  }
101
+ case 'detach': {
102
+ const { canonicalPath } = resolveManifestPaths(planEntry);
103
+ return removeEntry(manifest, canonicalPath, planEntry.provider);
104
+ }
101
105
  case 'skip': {
102
106
  return manifest;
103
107
  }
@@ -1 +1 @@
1
- {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/fs/io.ts"],"names":[],"mappings":"AAYA,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9C,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkB5E;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACrC,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf"}
1
+ {"version":3,"file":"io.d.ts","sourceRoot":"","sources":["../../src/fs/io.ts"],"names":[],"mappings":"AAaA,wBAAsB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO/D;AAED,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAO9D;AAED,MAAM,MAAM,YAAY,GAAG,SAAS,GAAG,MAAM,CAAC;AAE9C,wBAAsB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE9D;AAED,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsB5E;AAED,wBAAsB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7E;AAED,wBAAsB,aAAa,CACjC,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,UAAU,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,EACrC,MAAM,CAAC,EAAE,OAAO,GACf,OAAO,CAAC,YAAY,CAAC,CAuBvB;AAED,wBAAsB,eAAe,CACnC,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,OAAO,GACZ,OAAO,CAAC,IAAI,CAAC,CAKf"}
package/dist/fs/io.js CHANGED
@@ -1,4 +1,4 @@
1
- import { mkdir, readdir, readFile, rename, rm, stat, symlink, writeFile, } from 'node:fs/promises';
1
+ import { chmod, mkdir, readdir, readFile, rename, rm, stat, symlink, writeFile, } from 'node:fs/promises';
2
2
  import { dirname, isAbsolute, join, relative } from 'node:path';
3
3
  export async function fileExists(path) {
4
4
  try {
@@ -32,8 +32,12 @@ export async function copyDirectory(src, dest) {
32
32
  continue;
33
33
  }
34
34
  if (entry.isFile()) {
35
+ const sourceStat = await stat(sourcePath);
35
36
  const content = await readFile(sourcePath);
36
- await writeFile(destPath, content);
37
+ await writeFile(destPath, content, { mode: sourceStat.mode });
38
+ // writeFile's mode applies only on creation; chmod covers
39
+ // pre-existing destination files so modes stay in sync.
40
+ await chmod(destPath, sourceStat.mode);
37
41
  }
38
42
  }
39
43
  }
@@ -10,37 +10,37 @@ export declare const ManifestEntrySchema: z.ZodEffects<z.ZodObject<{
10
10
  lastSynced: z.ZodString;
11
11
  }, "strip", z.ZodTypeAny, {
12
12
  provider: string;
13
+ strategy: "symlink" | "copy";
13
14
  canonicalPath: string;
14
15
  providerPath: string;
15
16
  contentType: "skill" | "agent" | "rule";
16
- strategy: "symlink" | "copy";
17
17
  contentHash: string | null;
18
18
  isFile: boolean;
19
19
  lastSynced: string;
20
20
  }, {
21
21
  provider: string;
22
+ strategy: "symlink" | "copy";
22
23
  canonicalPath: string;
23
24
  providerPath: string;
24
25
  contentType: "skill" | "agent" | "rule";
25
- strategy: "symlink" | "copy";
26
26
  contentHash: string | null;
27
27
  lastSynced: string;
28
28
  isFile?: boolean | undefined;
29
29
  }>, {
30
30
  provider: string;
31
+ strategy: "symlink" | "copy";
31
32
  canonicalPath: string;
32
33
  providerPath: string;
33
34
  contentType: "skill" | "agent" | "rule";
34
- strategy: "symlink" | "copy";
35
35
  contentHash: string | null;
36
36
  isFile: boolean;
37
37
  lastSynced: string;
38
38
  }, {
39
39
  provider: string;
40
+ strategy: "symlink" | "copy";
40
41
  canonicalPath: string;
41
42
  providerPath: string;
42
43
  contentType: "skill" | "agent" | "rule";
43
- strategy: "symlink" | "copy";
44
44
  contentHash: string | null;
45
45
  lastSynced: string;
46
46
  isFile?: boolean | undefined;
@@ -59,37 +59,37 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
59
59
  lastSynced: z.ZodString;
60
60
  }, "strip", z.ZodTypeAny, {
61
61
  provider: string;
62
+ strategy: "symlink" | "copy";
62
63
  canonicalPath: string;
63
64
  providerPath: string;
64
65
  contentType: "skill" | "agent" | "rule";
65
- strategy: "symlink" | "copy";
66
66
  contentHash: string | null;
67
67
  isFile: boolean;
68
68
  lastSynced: string;
69
69
  }, {
70
70
  provider: string;
71
+ strategy: "symlink" | "copy";
71
72
  canonicalPath: string;
72
73
  providerPath: string;
73
74
  contentType: "skill" | "agent" | "rule";
74
- strategy: "symlink" | "copy";
75
75
  contentHash: string | null;
76
76
  lastSynced: string;
77
77
  isFile?: boolean | undefined;
78
78
  }>, {
79
79
  provider: string;
80
+ strategy: "symlink" | "copy";
80
81
  canonicalPath: string;
81
82
  providerPath: string;
82
83
  contentType: "skill" | "agent" | "rule";
83
- strategy: "symlink" | "copy";
84
84
  contentHash: string | null;
85
85
  isFile: boolean;
86
86
  lastSynced: string;
87
87
  }, {
88
88
  provider: string;
89
+ strategy: "symlink" | "copy";
89
90
  canonicalPath: string;
90
91
  providerPath: string;
91
92
  contentType: "skill" | "agent" | "rule";
92
- strategy: "symlink" | "copy";
93
93
  contentHash: string | null;
94
94
  lastSynced: string;
95
95
  isFile?: boolean | undefined;
@@ -98,10 +98,10 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
98
98
  }, "strip", z.ZodTypeAny, {
99
99
  entries: {
100
100
  provider: string;
101
+ strategy: "symlink" | "copy";
101
102
  canonicalPath: string;
102
103
  providerPath: string;
103
104
  contentType: "skill" | "agent" | "rule";
104
- strategy: "symlink" | "copy";
105
105
  contentHash: string | null;
106
106
  isFile: boolean;
107
107
  lastSynced: string;
@@ -112,10 +112,10 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
112
112
  }, {
113
113
  entries: {
114
114
  provider: string;
115
+ strategy: "symlink" | "copy";
115
116
  canonicalPath: string;
116
117
  providerPath: string;
117
118
  contentType: "skill" | "agent" | "rule";
118
- strategy: "symlink" | "copy";
119
119
  contentHash: string | null;
120
120
  lastSynced: string;
121
121
  isFile?: boolean | undefined;
@@ -126,10 +126,10 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
126
126
  }>, {
127
127
  entries: {
128
128
  provider: string;
129
+ strategy: "symlink" | "copy";
129
130
  canonicalPath: string;
130
131
  providerPath: string;
131
132
  contentType: "skill" | "agent" | "rule";
132
- strategy: "symlink" | "copy";
133
133
  contentHash: string | null;
134
134
  isFile: boolean;
135
135
  lastSynced: string;
@@ -140,10 +140,10 @@ export declare const ManifestSchema: z.ZodEffects<z.ZodObject<{
140
140
  }, {
141
141
  entries: {
142
142
  provider: string;
143
+ strategy: "symlink" | "copy";
143
144
  canonicalPath: string;
144
145
  providerPath: string;
145
146
  contentType: "skill" | "agent" | "rule";
146
- strategy: "symlink" | "copy";
147
147
  contentHash: string | null;
148
148
  lastSynced: string;
149
149
  isFile?: boolean | undefined;
@@ -1 +1 @@
1
- {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/providers/cursor/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAOnE,eAAO,MAAM,uBAAuB,EAAE,WAAW,EAsBhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,WAAW,EAa7C,CAAC"}
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../../src/providers/cursor/paths.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iCAAiC,CAAC;AAOnE,eAAO,MAAM,uBAAuB,EAAE,WAAW,EAuBhD,CAAC;AAEF,eAAO,MAAM,oBAAoB,EAAE,WAAW,EAc7C,CAAC"}
@@ -3,8 +3,9 @@ export const CURSOR_PROJECT_MAPPINGS = [
3
3
  {
4
4
  contentType: 'skill',
5
5
  canonicalDir: '.agents/skills',
6
- providerDir: '.cursor/skills',
7
- nativeRead: false,
6
+ providerDir: '.agents/skills',
7
+ nativeRead: true,
8
+ adoptionSourceDirs: ['.cursor/skills'],
8
9
  },
9
10
  {
10
11
  contentType: 'agent',
@@ -26,8 +27,9 @@ export const CURSOR_USER_MAPPINGS = [
26
27
  {
27
28
  contentType: 'skill',
28
29
  canonicalDir: '.agents/skills',
29
- providerDir: '.cursor/skills',
30
- nativeRead: false,
30
+ providerDir: '.agents/skills',
31
+ nativeRead: true,
32
+ adoptionSourceDirs: ['.cursor/skills'],
31
33
  },
32
34
  {
33
35
  contentType: 'agent',
@@ -4,10 +4,16 @@ export interface PathMapping {
4
4
  canonicalDir: string;
5
5
  providerDir: string;
6
6
  nativeRead: boolean;
7
+ adoptionSourceDirs?: string[];
7
8
  providerExtension?: string;
8
9
  transformCanonical?: (canonicalContent: string, canonicalPath?: string) => string;
9
10
  parseToCanonical?: (providerContent: string, providerPath?: string) => string;
10
11
  }
12
+ export interface AdoptionSource {
13
+ contentType: ContentType;
14
+ directory: string;
15
+ mapping: PathMapping;
16
+ }
11
17
  export interface ProviderAdapter {
12
18
  name: string;
13
19
  displayName: string;
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.types.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/adapter.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,CACnB,gBAAgB,EAAE,MAAM,EACxB,aAAa,CAAC,EAAE,MAAM,KACnB,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/E;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,YAAY,CAAC;IAC9B,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC"}
1
+ {"version":3,"file":"adapter.types.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/adapter.types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC;AAEtE,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,WAAW,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,OAAO,CAAC;IACpB,kBAAkB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC9B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,kBAAkB,CAAC,EAAE,CACnB,gBAAgB,EAAE,MAAM,EACxB,aAAa,CAAC,EAAE,MAAM,KACnB,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,CAAC,eAAe,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;CAC/E;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,YAAY,CAAC;IAC9B,eAAe,EAAE,WAAW,EAAE,CAAC;IAC/B,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,aAAa,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC9C;AAED,MAAM,MAAM,YAAY,GAAG,KAAK,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import type { SyncConfig } from '../../config/sync-config.js';
2
2
  import type { Scope } from '../../shared/types.js';
3
- import type { PathMapping, ProviderAdapter } from './adapter.types.js';
3
+ import type { AdoptionSource, PathMapping, ProviderAdapter } from './adapter.types.js';
4
4
  export declare function getActiveAdapters(adapters: ProviderAdapter[], scopeRoot: string): Promise<ProviderAdapter[]>;
5
5
  export interface ConfigAwareAdaptersResult {
6
6
  activeAdapters: ProviderAdapter[];
@@ -9,4 +9,5 @@ export interface ConfigAwareAdaptersResult {
9
9
  }
10
10
  export declare function getConfigAwareAdapters(adapters: ProviderAdapter[], scopeRoot: string, config: SyncConfig): Promise<ConfigAwareAdaptersResult>;
11
11
  export declare function getSyncMappings(adapter: ProviderAdapter, scope: Scope): PathMapping[];
12
+ export declare function getAdoptionSources(adapter: ProviderAdapter, scope: Scope): AdoptionSource[];
12
13
  //# sourceMappingURL=adapter.utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"adapter.utils.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/adapter.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAEpE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,EAAE,CAAC,CAW5B;AAED,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,eAAe,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAqCpC;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,KAAK,GACX,WAAW,EAAE,CAmBf"}
1
+ {"version":3,"file":"adapter.utils.d.ts","sourceRoot":"","sources":["../../../src/providers/shared/adapter.utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAE3C,OAAO,KAAK,EACV,cAAc,EACd,WAAW,EACX,eAAe,EAChB,MAAM,iBAAiB,CAAC;AAazB,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,eAAe,EAAE,CAAC,CAW5B;AAED,MAAM,WAAW,yBAAyB;IACxC,cAAc,EAAE,eAAe,EAAE,CAAC;IAClC,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,gBAAgB,EAAE,MAAM,EAAE,CAAC;CAC5B;AAED,wBAAsB,sBAAsB,CAC1C,QAAQ,EAAE,eAAe,EAAE,EAC3B,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,UAAU,GACjB,OAAO,CAAC,yBAAyB,CAAC,CAqCpC;AAED,wBAAgB,eAAe,CAC7B,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,KAAK,GACX,WAAW,EAAE,CAcf;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,eAAe,EACxB,KAAK,EAAE,KAAK,GACX,cAAc,EAAE,CAsBlB"}
@@ -1,3 +1,10 @@
1
+ function getScopeMappings(adapter, scope) {
2
+ return scope === 'project'
3
+ ? adapter.projectMappings
4
+ : scope === 'user'
5
+ ? adapter.userMappings
6
+ : [...adapter.projectMappings, ...adapter.userMappings];
7
+ }
1
8
  export async function getActiveAdapters(adapters, scopeRoot) {
2
9
  const detected = await Promise.all(adapters.map(async (adapter) => ({
3
10
  adapter,
@@ -39,12 +46,7 @@ export async function getConfigAwareAdapters(adapters, scopeRoot, config) {
39
46
  };
40
47
  }
41
48
  export function getSyncMappings(adapter, scope) {
42
- const scopeMappings = scope === 'project'
43
- ? adapter.projectMappings
44
- : scope === 'user'
45
- ? adapter.userMappings
46
- : [...adapter.projectMappings, ...adapter.userMappings];
47
- const syncMappings = scopeMappings.filter((mapping) => !mapping.nativeRead);
49
+ const syncMappings = getScopeMappings(adapter, scope).filter((mapping) => !mapping.nativeRead);
48
50
  const seen = new Set();
49
51
  return syncMappings.filter((mapping) => {
50
52
  const key = `${mapping.contentType}::${mapping.canonicalDir}::${mapping.providerDir}`;
@@ -55,3 +57,24 @@ export function getSyncMappings(adapter, scope) {
55
57
  return true;
56
58
  });
57
59
  }
60
+ export function getAdoptionSources(adapter, scope) {
61
+ const sources = getScopeMappings(adapter, scope).flatMap((mapping) => {
62
+ const directories = mapping.nativeRead
63
+ ? (mapping.adoptionSourceDirs ?? [])
64
+ : [mapping.providerDir];
65
+ return directories.map((directory) => ({
66
+ contentType: mapping.contentType,
67
+ directory,
68
+ mapping,
69
+ }));
70
+ });
71
+ const seen = new Set();
72
+ return sources.filter((source) => {
73
+ const key = `${source.contentType}::${source.directory}`;
74
+ if (seen.has(key)) {
75
+ return false;
76
+ }
77
+ seen.add(key);
78
+ return true;
79
+ });
80
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-agent-toolkit/cli",
3
- "version": "0.1.74",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "description": "Open Agent Toolkit CLI",
6
6
  "homepage": "https://github.com/voxmedia/open-agent-toolkit/tree/main/packages/cli",
@@ -34,7 +34,7 @@
34
34
  "ora": "^9.0.0",
35
35
  "yaml": "2.8.2",
36
36
  "zod": "^3.25.76",
37
- "@open-agent-toolkit/control-plane": "0.1.74"
37
+ "@open-agent-toolkit/control-plane": "0.2.0"
38
38
  },
39
39
  "devDependencies": {
40
40
  "@types/node": "^22.10.0",