@open-agent-toolkit/cli 0.1.27 → 0.1.29
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/docs/cli-utilities/tool-packs.md +6 -5
- package/assets/public-package-versions.json +4 -4
- package/assets/templates/docs-app-fuma/next.config.js +1 -3
- package/dist/commands/init/tools/index.d.ts +10 -0
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +239 -81
- package/package.json +2 -2
|
@@ -104,14 +104,15 @@ Key behavior:
|
|
|
104
104
|
- Same pack selection and install flow as `oat init tools`
|
|
105
105
|
- Pack-oriented install subcommands: `core`, `docs`, `ideas`, `workflows`, `utility`, `project-management`, `research`, `brainstorm`
|
|
106
106
|
- Interactive installs show each pack's current install location in the picker so already-installed packs are visible before you submit
|
|
107
|
-
-
|
|
108
|
-
-
|
|
109
|
-
-
|
|
110
|
-
-
|
|
107
|
+
- Installing is **additive**: choosing a scope for a pack never removes it from another scope. A user-eligible pack installed at user scope plus a project install ends up at `project + user`, not moved
|
|
108
|
+
- For each user-eligible pack (`ideas`, `docs`, `utility`, `research`, `brainstorm`), the interactive flow offers a per-pack end-state selector (`project`, `user`, or `both`) defaulting to the pack's current placement; leaving the default makes no changes for that pack
|
|
109
|
+
- The `brainstorm` pack defaults to user scope on fresh installs (driven by `PACK_METADATA[brainstorm].defaultScope = 'user'`); existing installs keep their current placement on re-install, so a re-install never moves a pack between scopes
|
|
110
|
+
- Removing a pack from a scope happens only when you explicitly choose a narrower end-state in the interactive flow (e.g. a pack at `both` set to `project` only). All staged removals are shown in a single change summary and applied only after one batch confirmation — declining makes no changes
|
|
111
|
+
- Non-interactive installs (including `--scope project`, `--scope user`, and the default pack set) are strictly additive and never remove a pack from a scope. Removal is interactive-only
|
|
111
112
|
- Tracks installed vs bundled skill versions and reports outdated skills
|
|
112
113
|
- Records installed pack state in shared repo config as `tools.<pack>: true` so other OAT workflows can detect installed capabilities without relying on filesystem heuristics
|
|
113
114
|
- Interactive runs can prompt to update selected outdated skills
|
|
114
|
-
- Successful installs report the final scope chosen for each pack, including `project + user` when
|
|
115
|
+
- Successful installs report the final scope chosen for each pack, including `project + user` when a pack is installed in both, and auto-sync only the scopes actually changed by the install so untouched scopes are never re-synced or pruned
|
|
115
116
|
- Install-triggered auto-sync limits removal planning to the canonical entries from the pack that was just installed, so stale manifest drift in unrelated packs does not delete other provider views
|
|
116
117
|
- Use `--no-sync` to skip auto-sync
|
|
117
118
|
|
|
@@ -2,6 +2,4 @@ import { createDocsConfig } from '@open-agent-toolkit/docs-config';
|
|
|
2
2
|
|
|
3
3
|
const basePath = process.env.NEXT_PUBLIC_BASE_PATH || undefined;
|
|
4
4
|
|
|
5
|
-
export default createDocsConfig({
|
|
6
|
-
...(basePath ? { basePath } : {}),
|
|
7
|
-
});
|
|
5
|
+
export default createDocsConfig(basePath ? { basePath } : {});
|
|
@@ -52,6 +52,16 @@ export interface InitToolsDependencies {
|
|
|
52
52
|
interface InitToolsRunMetadata {
|
|
53
53
|
affectedScopes: ConcreteScope[];
|
|
54
54
|
}
|
|
55
|
+
interface PackScopeChange {
|
|
56
|
+
pack: ToolPack;
|
|
57
|
+
scope: ConcreteScope;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Render a batch change summary (`+ pack@scope` adds / `- pack@scope` removes)
|
|
61
|
+
* for the interactive removal-confirmation gate. Pure for snapshot-friendly
|
|
62
|
+
* testing.
|
|
63
|
+
*/
|
|
64
|
+
export declare function formatReconcileSummary(adds: PackScopeChange[], removes: PackScopeChange[]): string;
|
|
55
65
|
export declare function consumeInitToolsRunMetadata(): InitToolsRunMetadata | null;
|
|
56
66
|
interface PackScopeInfo {
|
|
57
67
|
pack: ToolPack;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/init/tools/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/init/tools/index.ts"],"names":[],"mappings":"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"}
|
|
@@ -117,6 +117,77 @@ const USER_ELIGIBLE_PACKS = new Set([
|
|
|
117
117
|
function isUserEligiblePack(pack) {
|
|
118
118
|
return USER_ELIGIBLE_PACKS.has(pack);
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Concrete scopes implied by a desired end-state placement.
|
|
122
|
+
*/
|
|
123
|
+
function scopesForEndState(end) {
|
|
124
|
+
switch (end) {
|
|
125
|
+
case 'project':
|
|
126
|
+
return ['project'];
|
|
127
|
+
case 'user':
|
|
128
|
+
return ['user'];
|
|
129
|
+
case 'both':
|
|
130
|
+
return ['project', 'user'];
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Concrete scopes a pack is currently installed at.
|
|
135
|
+
*/
|
|
136
|
+
function scopesForLocation(location) {
|
|
137
|
+
switch (location) {
|
|
138
|
+
case 'project':
|
|
139
|
+
return ['project'];
|
|
140
|
+
case 'user':
|
|
141
|
+
return ['user'];
|
|
142
|
+
case 'both':
|
|
143
|
+
return ['project', 'user'];
|
|
144
|
+
default:
|
|
145
|
+
return [];
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Pure diff of current placement vs a desired end-state into the set of
|
|
150
|
+
* scopes to add (install into) and remove (delete from). This is the single
|
|
151
|
+
* source of truth that the reconciliation loop drives off of, so installs and
|
|
152
|
+
* removals only ever touch scopes that actually change.
|
|
153
|
+
*/
|
|
154
|
+
function reconcilePackScope(current, desired) {
|
|
155
|
+
const currentScopes = new Set(scopesForLocation(current));
|
|
156
|
+
const desiredScopes = new Set(scopesForEndState(desired));
|
|
157
|
+
const adds = [...desiredScopes].filter((scope) => !currentScopes.has(scope));
|
|
158
|
+
const removes = [...currentScopes].filter((scope) => !desiredScopes.has(scope));
|
|
159
|
+
return { adds, removes };
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Render a batch change summary (`+ pack@scope` adds / `- pack@scope` removes)
|
|
163
|
+
* for the interactive removal-confirmation gate. Pure for snapshot-friendly
|
|
164
|
+
* testing.
|
|
165
|
+
*/
|
|
166
|
+
export function formatReconcileSummary(adds, removes) {
|
|
167
|
+
const lines = ['Review pending changes:'];
|
|
168
|
+
for (const { pack, scope } of adds) {
|
|
169
|
+
lines.push(` + ${pack}@${scope}`);
|
|
170
|
+
}
|
|
171
|
+
for (const { pack, scope } of removes) {
|
|
172
|
+
lines.push(` - ${pack}@${scope}`);
|
|
173
|
+
}
|
|
174
|
+
return lines.join('\n');
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Additive union of a pack's current placement with a requested scope. Never
|
|
178
|
+
* returns a placement narrower than current — installing at one scope can
|
|
179
|
+
* never remove the pack from another scope.
|
|
180
|
+
*/
|
|
181
|
+
function unionScopeWithCurrent(current, requested) {
|
|
182
|
+
const scopes = new Set([
|
|
183
|
+
...scopesForLocation(current),
|
|
184
|
+
requested,
|
|
185
|
+
]);
|
|
186
|
+
if (scopes.has('project') && scopes.has('user')) {
|
|
187
|
+
return 'both';
|
|
188
|
+
}
|
|
189
|
+
return scopes.has('user') ? 'user' : 'project';
|
|
190
|
+
}
|
|
120
191
|
async function loadInstalledPackStates(projectRoot, userRoot, assetsRoot, dependencies) {
|
|
121
192
|
const [projectTools, userTools] = await Promise.all([
|
|
122
193
|
dependencies.scanTools({
|
|
@@ -191,43 +262,6 @@ function buildPackChoices(installedPackStates) {
|
|
|
191
262
|
},
|
|
192
263
|
];
|
|
193
264
|
}
|
|
194
|
-
function buildUserScopeChoices(packs, installedPackStates) {
|
|
195
|
-
return packs.map((pack) => {
|
|
196
|
-
const location = installedPackStates[pack].location;
|
|
197
|
-
// Existing-install detection wins over PACK_METADATA defaultScope so
|
|
198
|
-
// re-installs of an already-placed pack do not silently migrate the
|
|
199
|
-
// user across scopes. When the pack is not yet installed at any
|
|
200
|
-
// scope, fall back to PACK_METADATA[pack]?.defaultScope (defaults
|
|
201
|
-
// to 'project' when absent).
|
|
202
|
-
const checked = location === 'user' || location === 'both'
|
|
203
|
-
? true
|
|
204
|
-
: location === 'not-installed'
|
|
205
|
-
? resolvePackDefaultScope(pack) === 'user'
|
|
206
|
-
: false;
|
|
207
|
-
return {
|
|
208
|
-
label: location === 'not-installed'
|
|
209
|
-
? pack
|
|
210
|
-
: `${pack} (current: ${formatInstalledLocation(location)})`,
|
|
211
|
-
value: pack,
|
|
212
|
-
checked,
|
|
213
|
-
};
|
|
214
|
-
});
|
|
215
|
-
}
|
|
216
|
-
async function resolveBothScopeTarget(pack, dependencies, interactive) {
|
|
217
|
-
const selection = await dependencies.selectWithAbort(`${pack} is currently installed in project and user scope. Keep both installs or normalize to user scope?`, [
|
|
218
|
-
{
|
|
219
|
-
label: 'Keep project + user (recommended)',
|
|
220
|
-
value: 'both',
|
|
221
|
-
description: 'Preserve both installed copies',
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
label: 'User only',
|
|
225
|
-
value: 'user',
|
|
226
|
-
description: 'Remove the project-scoped copy',
|
|
227
|
-
},
|
|
228
|
-
], { interactive });
|
|
229
|
-
return selection ?? 'both';
|
|
230
|
-
}
|
|
231
265
|
export function consumeInitToolsRunMetadata() {
|
|
232
266
|
const metadata = lastRunInitToolsMetadata;
|
|
233
267
|
lastRunInitToolsMetadata = null;
|
|
@@ -258,16 +292,13 @@ async function resolvePackScopes(context, selections, installedPackStates, depen
|
|
|
258
292
|
if (eligiblePacks.length === 0) {
|
|
259
293
|
return scopes;
|
|
260
294
|
}
|
|
261
|
-
// Explicit --scope
|
|
262
|
-
|
|
295
|
+
// Explicit --scope is additive: union the requested scope with the pack's
|
|
296
|
+
// current placement so installing at one scope never removes another. A pack
|
|
297
|
+
// currently at `user` installed with `--scope project` becomes `both`.
|
|
298
|
+
if (context.scope === 'project' || context.scope === 'user') {
|
|
299
|
+
const requested = context.scope;
|
|
263
300
|
for (const pack of eligiblePacks) {
|
|
264
|
-
scopes[pack] =
|
|
265
|
-
}
|
|
266
|
-
return scopes;
|
|
267
|
-
}
|
|
268
|
-
if (context.scope === 'user') {
|
|
269
|
-
for (const pack of eligiblePacks) {
|
|
270
|
-
scopes[pack] = 'user';
|
|
301
|
+
scopes[pack] = unionScopeWithCurrent(installedPackStates[pack].location, requested);
|
|
271
302
|
}
|
|
272
303
|
return scopes;
|
|
273
304
|
}
|
|
@@ -298,21 +329,70 @@ async function resolvePackScopes(context, selections, installedPackStates, depen
|
|
|
298
329
|
}
|
|
299
330
|
return scopes;
|
|
300
331
|
}
|
|
301
|
-
// Interactive:
|
|
302
|
-
|
|
303
|
-
|
|
332
|
+
// Interactive: for each user-eligible pack, choose its desired end-state
|
|
333
|
+
// placement (project / user / both). The default offered is the pack's
|
|
334
|
+
// current placement (or its default scope when not yet installed), so
|
|
335
|
+
// breezing through accepting defaults is a no-op and never removes a scope.
|
|
304
336
|
for (const pack of eligiblePacks) {
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
scopes[pack] =
|
|
310
|
-
installedPackStates[pack].location === 'both'
|
|
311
|
-
? await resolveBothScopeTarget(pack, dependencies, context.interactive)
|
|
312
|
-
: 'user';
|
|
337
|
+
const currentLocation = installedPackStates[pack].location;
|
|
338
|
+
const defaultEndState = resolvePackDefaultEndState(pack, currentLocation);
|
|
339
|
+
const selection = await dependencies.selectWithAbort(`Where should ${pack} install?`, buildPackEndStateChoices(pack, currentLocation, defaultEndState), { interactive: context.interactive });
|
|
340
|
+
scopes[pack] = selection ?? defaultEndState;
|
|
313
341
|
}
|
|
314
342
|
return scopes;
|
|
315
343
|
}
|
|
344
|
+
/**
|
|
345
|
+
* Default end-state offered for a pack in the interactive selector: its
|
|
346
|
+
* current placement when installed, or its configured default scope when not
|
|
347
|
+
* yet present.
|
|
348
|
+
*/
|
|
349
|
+
function resolvePackDefaultEndState(pack, currentLocation) {
|
|
350
|
+
switch (currentLocation) {
|
|
351
|
+
case 'project':
|
|
352
|
+
return 'project';
|
|
353
|
+
case 'user':
|
|
354
|
+
return 'user';
|
|
355
|
+
case 'both':
|
|
356
|
+
return 'both';
|
|
357
|
+
default:
|
|
358
|
+
return resolvePackDefaultScope(pack);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Per-pack end-state options (project / user / both) for the interactive
|
|
363
|
+
* selector. The default end-state is listed first so the underlying
|
|
364
|
+
* single-select highlights it.
|
|
365
|
+
*/
|
|
366
|
+
function buildPackEndStateChoices(pack, currentLocation, defaultEndState) {
|
|
367
|
+
const currentLabel = currentLocation === 'not-installed'
|
|
368
|
+
? 'not installed'
|
|
369
|
+
: `current: ${formatInstalledLocation(currentLocation)}`;
|
|
370
|
+
const baseChoices = [
|
|
371
|
+
{
|
|
372
|
+
label: `Project scope (${pack})`,
|
|
373
|
+
value: 'project',
|
|
374
|
+
description: 'Install at project scope only',
|
|
375
|
+
},
|
|
376
|
+
{
|
|
377
|
+
label: `User scope (${pack})`,
|
|
378
|
+
value: 'user',
|
|
379
|
+
description: 'Install at user scope only',
|
|
380
|
+
},
|
|
381
|
+
{
|
|
382
|
+
label: `Project + user (${pack})`,
|
|
383
|
+
value: 'both',
|
|
384
|
+
description: 'Install at both scopes',
|
|
385
|
+
},
|
|
386
|
+
];
|
|
387
|
+
// Annotate the option matching the default so the prompt communicates the
|
|
388
|
+
// current placement, and order it first so it is the highlighted default.
|
|
389
|
+
const annotated = baseChoices.map((choice) => choice.value === defaultEndState
|
|
390
|
+
? { ...choice, label: `${choice.label} [${currentLabel}]` }
|
|
391
|
+
: choice);
|
|
392
|
+
const defaultChoice = annotated.find((choice) => choice.value === defaultEndState);
|
|
393
|
+
const rest = annotated.filter((choice) => choice.value !== defaultEndState);
|
|
394
|
+
return defaultChoice ? [defaultChoice, ...rest] : annotated;
|
|
395
|
+
}
|
|
316
396
|
function buildInstalledToolsConfig(selectedPacks, installedPackStates, existingTools) {
|
|
317
397
|
const selectedPackSet = new Set(selectedPacks);
|
|
318
398
|
const tools = { ...existingTools };
|
|
@@ -428,37 +508,89 @@ export async function runInitTools(context, dependencies) {
|
|
|
428
508
|
return [];
|
|
429
509
|
}
|
|
430
510
|
const packScopes = await resolvePackScopes(context, selectedPacks, initialPackStates, dependencies);
|
|
431
|
-
function
|
|
432
|
-
return
|
|
511
|
+
function scopeRoot(scope) {
|
|
512
|
+
return scope === 'user' ? userRoot : projectRoot;
|
|
513
|
+
}
|
|
514
|
+
// Reconcile current placement vs the desired end-state per user-eligible
|
|
515
|
+
// pack into adds/removes. Installs copy idempotently into the full desired
|
|
516
|
+
// end-state, but only the scopes that actually changed (an add or a
|
|
517
|
+
// confirmed remove) are recorded in `affectedScopes`, so auto-sync never
|
|
518
|
+
// prunes a preserved scope.
|
|
519
|
+
const reconciliationByPack = new Map();
|
|
520
|
+
for (const pack of selectedPacks) {
|
|
521
|
+
if (!isUserEligiblePack(pack)) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
reconciliationByPack.set(pack, reconcilePackScope(initialPackStates[pack].location, packScopes[pack]));
|
|
433
525
|
}
|
|
526
|
+
// Scopes a pack should install into (its full desired end-state for
|
|
527
|
+
// user-eligible packs; the resolved scope otherwise). Re-installing an
|
|
528
|
+
// already-present scope is an idempotent copy.
|
|
434
529
|
function packTargets(pack) {
|
|
435
530
|
return packScopes[pack] === 'both'
|
|
436
531
|
? [projectRoot, userRoot]
|
|
437
|
-
: [
|
|
532
|
+
: [packScopes[pack] === 'user' ? userRoot : projectRoot];
|
|
533
|
+
}
|
|
534
|
+
// Only scopes that received a new add for this pack should be auto-synced.
|
|
535
|
+
function addedScopes(pack) {
|
|
536
|
+
return new Set(reconciliationByPack.get(pack)?.adds);
|
|
438
537
|
}
|
|
439
538
|
const outdatedSkills = [];
|
|
440
539
|
const affectedScopes = new Set();
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
540
|
+
// Collect all staged removals across packs. Removals are interactive-only
|
|
541
|
+
// and gated behind a single batch confirmation (added in p01-t03);
|
|
542
|
+
// non-interactive paths are strictly additive and can never remove.
|
|
543
|
+
const stagedRemovals = [];
|
|
544
|
+
for (const [pack, reconciliation] of reconciliationByPack) {
|
|
545
|
+
for (const scope of reconciliation.removes) {
|
|
546
|
+
stagedRemovals.push({ pack, scope });
|
|
444
547
|
}
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
548
|
+
}
|
|
549
|
+
if (stagedRemovals.length > 0 && !context.interactive) {
|
|
550
|
+
// Strictly-additive guard: removals must never be applied
|
|
551
|
+
// non-interactively. This should be unreachable because non-interactive
|
|
552
|
+
// resolution unions with current placement, but we fail loud rather than
|
|
553
|
+
// silently delete a user's install.
|
|
554
|
+
throw new Error('Non-interactive install attempted to remove a pack from a scope; ' +
|
|
555
|
+
'install is strictly additive in non-interactive mode.');
|
|
556
|
+
}
|
|
557
|
+
// Interactive removal gate: if any pack would lose a scope, surface one
|
|
558
|
+
// batch change summary and require a single confirmation before mutating
|
|
559
|
+
// anything. Declining aborts with zero changes (no installs, no removals).
|
|
560
|
+
if (stagedRemovals.length > 0 && context.interactive) {
|
|
561
|
+
const stagedAdds = [];
|
|
562
|
+
for (const [pack, reconciliation] of reconciliationByPack) {
|
|
563
|
+
for (const scope of reconciliation.adds) {
|
|
564
|
+
stagedAdds.push({ pack, scope });
|
|
454
565
|
}
|
|
455
|
-
continue;
|
|
456
566
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
567
|
+
context.logger.info(formatReconcileSummary(stagedAdds, stagedRemovals));
|
|
568
|
+
const confirmation = await dependencies.selectWithAbort('Apply these changes? Removals will delete the listed scoped installs.', [
|
|
569
|
+
{
|
|
570
|
+
label: 'No, cancel (recommended)',
|
|
571
|
+
value: 'no',
|
|
572
|
+
description: 'Make no changes',
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
label: 'Yes, apply adds and removals',
|
|
576
|
+
value: 'yes',
|
|
577
|
+
description: 'Install adds and delete the listed removals',
|
|
578
|
+
},
|
|
579
|
+
], { interactive: context.interactive });
|
|
580
|
+
if (confirmation !== 'yes') {
|
|
581
|
+
lastRunInitToolsMetadata = { affectedScopes: [] };
|
|
582
|
+
if (!context.json) {
|
|
583
|
+
context.logger.info('No changes applied.');
|
|
584
|
+
}
|
|
585
|
+
process.exitCode = 0;
|
|
586
|
+
return [];
|
|
460
587
|
}
|
|
461
588
|
}
|
|
589
|
+
// Removals are deferred until after the add phase below: for a confirmed
|
|
590
|
+
// move (e.g. user -> project), the replacement install must succeed before
|
|
591
|
+
// the preserved scope is deleted. If any installer throws, the catch
|
|
592
|
+
// handler aborts and these removals never run, so the pack is never left
|
|
593
|
+
// installed in neither scope (review I1; design.md:86/114).
|
|
462
594
|
if (selectedPacks.includes('core')) {
|
|
463
595
|
// Core pack always installs at user scope, regardless of userEligibleScope
|
|
464
596
|
affectedScopes.add('user');
|
|
@@ -475,8 +607,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
475
607
|
}
|
|
476
608
|
}
|
|
477
609
|
if (selectedPacks.includes('ideas')) {
|
|
610
|
+
const ideasAdded = addedScopes('ideas');
|
|
478
611
|
for (const targetRoot of packTargets('ideas')) {
|
|
479
|
-
|
|
612
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
613
|
+
if (ideasAdded.has(targetScope)) {
|
|
614
|
+
affectedScopes.add(targetScope);
|
|
615
|
+
}
|
|
480
616
|
const ideasResult = await dependencies.installIdeas({
|
|
481
617
|
assetsRoot,
|
|
482
618
|
targetRoot,
|
|
@@ -491,8 +627,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
491
627
|
}
|
|
492
628
|
}
|
|
493
629
|
if (selectedPacks.includes('docs')) {
|
|
630
|
+
const docsAdded = addedScopes('docs');
|
|
494
631
|
for (const targetRoot of packTargets('docs')) {
|
|
495
|
-
|
|
632
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
633
|
+
if (docsAdded.has(targetScope)) {
|
|
634
|
+
affectedScopes.add(targetScope);
|
|
635
|
+
}
|
|
496
636
|
const docsResult = await dependencies.installDocs({
|
|
497
637
|
assetsRoot,
|
|
498
638
|
targetRoot,
|
|
@@ -557,8 +697,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
557
697
|
}
|
|
558
698
|
}
|
|
559
699
|
if (selectedPacks.includes('utility')) {
|
|
700
|
+
const utilityAdded = addedScopes('utility');
|
|
560
701
|
for (const targetRoot of packTargets('utility')) {
|
|
561
|
-
|
|
702
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
703
|
+
if (utilityAdded.has(targetScope)) {
|
|
704
|
+
affectedScopes.add(targetScope);
|
|
705
|
+
}
|
|
562
706
|
const utilityResult = await dependencies.installUtility({
|
|
563
707
|
assetsRoot,
|
|
564
708
|
targetRoot,
|
|
@@ -589,8 +733,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
589
733
|
}
|
|
590
734
|
}
|
|
591
735
|
if (selectedPacks.includes('research')) {
|
|
736
|
+
const researchAdded = addedScopes('research');
|
|
592
737
|
for (const targetRoot of packTargets('research')) {
|
|
593
|
-
|
|
738
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
739
|
+
if (researchAdded.has(targetScope)) {
|
|
740
|
+
affectedScopes.add(targetScope);
|
|
741
|
+
}
|
|
594
742
|
const researchResult = await dependencies.installResearch({
|
|
595
743
|
assetsRoot,
|
|
596
744
|
targetRoot,
|
|
@@ -606,8 +754,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
606
754
|
}
|
|
607
755
|
}
|
|
608
756
|
if (selectedPacks.includes('brainstorm')) {
|
|
757
|
+
const brainstormAdded = addedScopes('brainstorm');
|
|
609
758
|
for (const targetRoot of packTargets('brainstorm')) {
|
|
610
|
-
|
|
759
|
+
const targetScope = targetRoot === userRoot ? 'user' : 'project';
|
|
760
|
+
if (brainstormAdded.has(targetScope)) {
|
|
761
|
+
affectedScopes.add(targetScope);
|
|
762
|
+
}
|
|
611
763
|
const brainstormResult = await dependencies.installBrainstorm({
|
|
612
764
|
assetsRoot,
|
|
613
765
|
targetRoot,
|
|
@@ -621,6 +773,12 @@ export async function runInitTools(context, dependencies) {
|
|
|
621
773
|
}
|
|
622
774
|
}
|
|
623
775
|
}
|
|
776
|
+
// Apply confirmed removals only after every add has succeeded, so a failed
|
|
777
|
+
// replacement install can never leave a pack uninstalled in both scopes.
|
|
778
|
+
for (const { pack, scope } of stagedRemovals) {
|
|
779
|
+
await removePackFromScope(pack, scopeRoot(scope), dependencies);
|
|
780
|
+
affectedScopes.add(scope);
|
|
781
|
+
}
|
|
624
782
|
if (outdatedSkills.length > 0) {
|
|
625
783
|
reportOutdatedSkills(context, outdatedSkills);
|
|
626
784
|
if (context.interactive) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@open-agent-toolkit/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
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.
|
|
37
|
+
"@open-agent-toolkit/control-plane": "0.1.29"
|
|
38
38
|
},
|
|
39
39
|
"devDependencies": {
|
|
40
40
|
"@types/node": "^22.10.0",
|