@open-agent-toolkit/cli 0.2.36 → 0.2.37
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/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/backlog-lifecycle.md +18 -0
- package/assets/docs/cli-utilities/bootstrap.md +6 -3
- package/assets/docs/cli-utilities/config-and-local-state.md +26 -12
- package/assets/docs/cli-utilities/configuration.md +36 -13
- package/assets/docs/cli-utilities/tool-packs.md +423 -51
- package/assets/docs/provider-sync/scope-and-surface.md +4 -1
- package/assets/docs/reference/cli-reference.md +5 -3
- package/assets/docs/reference/file-locations.md +28 -4
- package/assets/docs/reference/troubleshooting.md +132 -2
- package/assets/docs/workflows/projects/lifecycle.md +21 -2
- package/assets/public-package-versions.json +4 -4
- package/assets/scripts/generate-oat-state.sh +441 -0
- package/assets/scripts/generate-thin-index.sh +200 -0
- package/assets/skills/oat-agent-instructions-analyze/SKILL.md +10 -5
- package/assets/skills/oat-agent-instructions-apply/SKILL.md +30 -5
- package/assets/skills/oat-brainstorm/SKILL.md +34 -8
- package/assets/skills/oat-brainstorm/references/destinations.md +6 -1
- package/assets/skills/oat-docs-analyze/SKILL.md +9 -4
- package/assets/skills/oat-docs-apply/SKILL.md +6 -3
- package/assets/skills/oat-pjm-add-backlog-item/SKILL.md +14 -1
- package/assets/skills/oat-pjm-decision/SKILL.md +22 -10
- package/assets/skills/oat-pjm-review-backlog/SKILL.md +20 -3
- package/assets/skills/oat-pjm-update-repo-reference/SKILL.md +20 -10
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
- package/assets/skills/oat-project-document/SKILL.md +21 -3
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
- package/assets/skills/oat-project-summary/SKILL.md +23 -11
- package/assets/skills/oat-repo-knowledge-index/SKILL.md +6 -3
- package/dist/commands/backlog/index.d.ts +2 -0
- package/dist/commands/backlog/index.d.ts.map +1 -1
- package/dist/commands/backlog/index.js +67 -23
- package/dist/commands/backlog/new.d.ts +3 -0
- package/dist/commands/backlog/new.d.ts.map +1 -1
- package/dist/commands/backlog/new.js +8 -21
- package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
- package/dist/commands/decision/agents-guidance.js +2 -1
- package/dist/commands/decision/index.d.ts +2 -0
- package/dist/commands/decision/index.d.ts.map +1 -1
- package/dist/commands/decision/index.js +19 -3
- package/dist/commands/decision/new.d.ts +4 -0
- package/dist/commands/decision/new.d.ts.map +1 -1
- package/dist/commands/decision/new.js +11 -30
- package/dist/commands/doctor/index.d.ts +4 -0
- package/dist/commands/doctor/index.d.ts.map +1 -1
- package/dist/commands/doctor/index.js +254 -9
- package/dist/commands/init/tools/brainstorm/index.d.ts.map +1 -1
- package/dist/commands/init/tools/brainstorm/index.js +20 -6
- package/dist/commands/init/tools/docs/index.d.ts.map +1 -1
- package/dist/commands/init/tools/docs/index.js +4 -1
- package/dist/commands/init/tools/ideas/index.d.ts.map +1 -1
- package/dist/commands/init/tools/ideas/index.js +4 -1
- package/dist/commands/init/tools/index.d.ts +11 -2
- package/dist/commands/init/tools/index.d.ts.map +1 -1
- package/dist/commands/init/tools/index.js +459 -351
- package/dist/commands/init/tools/install-state.d.ts +2 -0
- package/dist/commands/init/tools/install-state.d.ts.map +1 -1
- package/dist/commands/init/tools/install-state.js +11 -0
- package/dist/commands/init/tools/project-management/agents-guidance.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/agents-guidance.js +3 -1
- package/dist/commands/init/tools/project-management/index.d.ts +1 -0
- package/dist/commands/init/tools/project-management/index.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/index.js +24 -55
- package/dist/commands/init/tools/project-management/install-project-management.d.ts +4 -0
- package/dist/commands/init/tools/project-management/install-project-management.d.ts.map +1 -1
- package/dist/commands/init/tools/project-management/install-project-management.js +4 -1
- package/dist/commands/init/tools/research/index.d.ts.map +1 -1
- package/dist/commands/init/tools/research/index.js +4 -1
- package/dist/commands/init/tools/shared/copy-helpers.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/copy-helpers.js +10 -0
- package/dist/commands/init/tools/shared/skill-manifest.d.ts +15 -15
- package/dist/commands/init/tools/shared/skill-manifest.d.ts.map +1 -1
- package/dist/commands/init/tools/shared/skill-manifest.js +17 -122
- package/dist/commands/init/tools/utility/index.d.ts.map +1 -1
- package/dist/commands/init/tools/utility/index.js +4 -1
- package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/index.js +4 -1
- package/dist/commands/init/tools/workflows/install-workflows.d.ts.map +1 -1
- package/dist/commands/init/tools/workflows/install-workflows.js +2 -4
- package/dist/commands/pjm/adoption.d.ts +12 -0
- package/dist/commands/pjm/adoption.d.ts.map +1 -0
- package/dist/commands/pjm/adoption.js +47 -0
- package/dist/commands/pjm/doctor.d.ts +3 -2
- package/dist/commands/pjm/doctor.d.ts.map +1 -1
- package/dist/commands/pjm/doctor.js +28 -13
- package/dist/commands/pjm/index.d.ts +2 -0
- package/dist/commands/pjm/index.d.ts.map +1 -1
- package/dist/commands/pjm/index.js +11 -3
- package/dist/commands/pjm/init.d.ts +3 -0
- package/dist/commands/pjm/init.d.ts.map +1 -1
- package/dist/commands/pjm/init.js +37 -32
- package/dist/commands/pjm/migrate.d.ts +1 -0
- package/dist/commands/pjm/migrate.d.ts.map +1 -1
- package/dist/commands/pjm/migrate.js +1 -0
- package/dist/commands/pjm/template-source.d.ts +14 -0
- package/dist/commands/pjm/template-source.d.ts.map +1 -0
- package/dist/commands/pjm/template-source.js +56 -0
- package/dist/commands/remove/skills/remove-skills.d.ts.map +1 -1
- package/dist/commands/remove/skills/remove-skills.js +10 -19
- package/dist/commands/status/index.d.ts +5 -0
- package/dist/commands/status/index.d.ts.map +1 -1
- package/dist/commands/status/index.js +203 -2
- package/dist/commands/sync/index.d.ts.map +1 -1
- package/dist/commands/sync/index.js +40 -7
- package/dist/commands/sync/sync.types.d.ts +4 -0
- package/dist/commands/sync/sync.types.d.ts.map +1 -1
- package/dist/commands/tools/has/has-pack.d.ts +11 -2
- package/dist/commands/tools/has/has-pack.d.ts.map +1 -1
- package/dist/commands/tools/has/has-pack.js +33 -20
- package/dist/commands/tools/index.d.ts.map +1 -1
- package/dist/commands/tools/index.js +3 -1
- package/dist/commands/tools/info/info-tool.d.ts +3 -0
- package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
- package/dist/commands/tools/info/info-tool.js +29 -4
- package/dist/commands/tools/list/list-tools.d.ts +3 -0
- package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
- package/dist/commands/tools/list/list-tools.js +31 -4
- package/dist/commands/tools/migrate/index.d.ts +26 -0
- package/dist/commands/tools/migrate/index.d.ts.map +1 -0
- package/dist/commands/tools/migrate/index.js +312 -0
- package/dist/commands/tools/migrate/migrate-pack.d.ts +81 -0
- package/dist/commands/tools/migrate/migrate-pack.d.ts.map +1 -0
- package/dist/commands/tools/migrate/migrate-pack.js +324 -0
- package/dist/commands/tools/outdated/outdated-tools.d.ts +11 -0
- package/dist/commands/tools/outdated/outdated-tools.d.ts.map +1 -1
- package/dist/commands/tools/outdated/outdated-tools.js +33 -5
- package/dist/commands/tools/remove/index.d.ts.map +1 -1
- package/dist/commands/tools/remove/index.js +73 -20
- package/dist/commands/tools/remove/remove-tools.d.ts +28 -0
- package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
- package/dist/commands/tools/remove/remove-tools.js +156 -26
- package/dist/commands/tools/shared/apply-pack-reconcile.d.ts +41 -0
- package/dist/commands/tools/shared/apply-pack-reconcile.d.ts.map +1 -0
- package/dist/commands/tools/shared/apply-pack-reconcile.js +126 -0
- package/dist/commands/tools/shared/auto-sync.d.ts +5 -2
- package/dist/commands/tools/shared/auto-sync.d.ts.map +1 -1
- package/dist/commands/tools/shared/auto-sync.js +1 -0
- package/dist/commands/tools/shared/content-digest.d.ts +3 -0
- package/dist/commands/tools/shared/content-digest.d.ts.map +1 -0
- package/dist/commands/tools/shared/content-digest.js +57 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts +3 -0
- package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -0
- package/dist/commands/tools/shared/format-pack-inventory.js +21 -0
- package/dist/commands/tools/shared/install-sync-context.d.ts.map +1 -1
- package/dist/commands/tools/shared/install-sync-context.js +2 -25
- package/dist/commands/tools/shared/pack-inventory.d.ts +46 -0
- package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-inventory.js +207 -0
- package/dist/commands/tools/shared/pack-lifecycle.d.ts +34 -0
- package/dist/commands/tools/shared/pack-lifecycle.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-lifecycle.js +67 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts +11 -0
- package/dist/commands/tools/shared/pack-manifest.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-manifest.js +384 -0
- package/dist/commands/tools/shared/pack-paths.d.ts +21 -0
- package/dist/commands/tools/shared/pack-paths.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-paths.js +35 -0
- package/dist/commands/tools/shared/pack-reconcile.d.ts +64 -0
- package/dist/commands/tools/shared/pack-reconcile.d.ts.map +1 -0
- package/dist/commands/tools/shared/pack-reconcile.js +158 -0
- package/dist/commands/tools/shared/project-tools-config.d.ts.map +1 -1
- package/dist/commands/tools/shared/project-tools-config.js +12 -38
- package/dist/commands/tools/shared/scan-tools.js +2 -2
- package/dist/commands/tools/shared/scoped-pack-intent.d.ts +34 -0
- package/dist/commands/tools/shared/scoped-pack-intent.d.ts.map +1 -0
- package/dist/commands/tools/shared/scoped-pack-intent.js +116 -0
- package/dist/commands/tools/shared/types.d.ts +22 -0
- package/dist/commands/tools/shared/types.d.ts.map +1 -1
- package/dist/commands/tools/update/index.d.ts.map +1 -1
- package/dist/commands/tools/update/index.js +30 -5
- package/dist/commands/tools/update/update-tools.d.ts +9 -1
- package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
- package/dist/commands/tools/update/update-tools.js +176 -64
- package/dist/config/oat-config.d.ts +6 -0
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +49 -20
- package/dist/fs/paths.d.ts +12 -0
- package/dist/fs/paths.d.ts.map +1 -1
- package/dist/fs/paths.js +87 -1
- package/package.json +2 -2
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
import { applyPackReconcilePlan, } from '../../tools/shared/apply-pack-reconcile.js';
|
|
2
|
+
import { hasScopedPackPlacementEvidence, } from '../../tools/shared/pack-inventory.js';
|
|
3
|
+
import { getPackDefinition } from '../../tools/shared/pack-manifest.js';
|
|
4
|
+
import { planPackReconcile, } from '../../tools/shared/pack-reconcile.js';
|
|
5
|
+
function assertInventory(inventory, pack, scope, label) {
|
|
6
|
+
if (inventory.pack !== pack || inventory.scope !== scope) {
|
|
7
|
+
throw new Error(`Migration ${label} inventory does not match pack ${pack} at ${scope} scope`);
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
function previewAsset(asset, scope, reason) {
|
|
11
|
+
return {
|
|
12
|
+
assetId: asset.definition.id,
|
|
13
|
+
kind: asset.definition.kind,
|
|
14
|
+
scope,
|
|
15
|
+
path: asset.path,
|
|
16
|
+
status: asset.status,
|
|
17
|
+
...(reason ? { reason } : {}),
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
export function planPackMigration(input) {
|
|
21
|
+
if (input.from === input.to) {
|
|
22
|
+
throw new Error('Migration source and destination scopes must differ');
|
|
23
|
+
}
|
|
24
|
+
const packDefinition = getPackDefinition(input.pack);
|
|
25
|
+
if (!packDefinition.allowedScopes.includes(input.from)) {
|
|
26
|
+
throw new Error(`Pack ${input.pack} does not allow ${input.from} source scope`);
|
|
27
|
+
}
|
|
28
|
+
if (!packDefinition.allowedScopes.includes(input.to)) {
|
|
29
|
+
throw new Error(`Pack ${input.pack} does not allow ${input.to} destination scope`);
|
|
30
|
+
}
|
|
31
|
+
assertInventory(input.sourceInventory, input.pack, input.from, 'source');
|
|
32
|
+
assertInventory(input.destinationInventory, input.pack, input.to, 'destination');
|
|
33
|
+
if (!hasScopedPackPlacementEvidence(input.sourceInventory)) {
|
|
34
|
+
throw new Error(`Pack ${input.pack} is not installed at ${input.from} scope`);
|
|
35
|
+
}
|
|
36
|
+
const conflicts = input.destinationInventory.assets.filter(({ definition: asset, status }) => asset.ownership[input.to] === 'managed' && status === 'newer');
|
|
37
|
+
const unfilteredDestinationPlan = planPackReconcile({
|
|
38
|
+
pack: input.pack,
|
|
39
|
+
scope: input.to,
|
|
40
|
+
scopeRoot: input.destinationRoot,
|
|
41
|
+
assetsRoot: input.assetsRoot,
|
|
42
|
+
action: 'migrate-destination',
|
|
43
|
+
inventory: input.destinationInventory,
|
|
44
|
+
});
|
|
45
|
+
const conflictIds = new Set(conflicts.map(({ definition: asset }) => asset.id));
|
|
46
|
+
const conflictCanonicalPaths = new Set(conflicts.flatMap(({ definition: asset }) => asset.kind === 'skill' || asset.kind === 'agent'
|
|
47
|
+
? [asset.destination]
|
|
48
|
+
: []));
|
|
49
|
+
const destinationPlan = {
|
|
50
|
+
...unfilteredDestinationPlan,
|
|
51
|
+
operations: unfilteredDestinationPlan.operations.filter((operation) => operation.kind === 'write-intent' ||
|
|
52
|
+
!conflictIds.has(operation.assetId)),
|
|
53
|
+
changedCanonicalPaths: unfilteredDestinationPlan.changedCanonicalPaths.filter((path) => !conflictCanonicalPaths.has(path)),
|
|
54
|
+
};
|
|
55
|
+
const additionIds = new Set(destinationPlan.operations.flatMap((operation) => operation.kind === 'write-intent' ? [] : [operation.assetId]));
|
|
56
|
+
const additions = input.destinationInventory.assets
|
|
57
|
+
.filter(({ definition }) => additionIds.has(definition.id) && !conflictIds.has(definition.id))
|
|
58
|
+
.map((asset) => previewAsset(asset, input.to, 'destination-reconcile'));
|
|
59
|
+
const duplicates = input.destinationInventory.assets
|
|
60
|
+
.filter(({ definition: asset, status }) => asset.ownership[input.to] === 'managed' && status === 'current')
|
|
61
|
+
.map((asset) => previewAsset(asset, input.to, 'already-current'));
|
|
62
|
+
const retainedIds = new Set((input.sourceRetentions ?? []).map(({ assetId }) => assetId));
|
|
63
|
+
const removals = input.sourceInventory.assets
|
|
64
|
+
.filter(({ definition: asset, status }) => asset.ownership[input.from] === 'managed' &&
|
|
65
|
+
status !== 'missing' &&
|
|
66
|
+
!retainedIds.has(asset.id))
|
|
67
|
+
.map((asset) => previewAsset(asset, input.from, 'source-managed'));
|
|
68
|
+
const retained = input.sourceInventory.assets
|
|
69
|
+
.filter(({ definition: asset, status }) => asset.ownership[input.from] === 'seed-if-missing' &&
|
|
70
|
+
status !== 'missing')
|
|
71
|
+
.map((asset) => previewAsset(asset, input.from, 'scope-owner-data'))
|
|
72
|
+
.concat(input.destinationInventory.assets
|
|
73
|
+
.filter(({ definition: asset, status }) => asset.ownership[input.to] === 'seed-if-missing' &&
|
|
74
|
+
status !== 'missing')
|
|
75
|
+
.map((asset) => previewAsset(asset, input.to, 'scope-owner-data')))
|
|
76
|
+
.concat((input.sourceRetentions ?? []).flatMap((retention) => {
|
|
77
|
+
const asset = input.sourceInventory.assets.find(({ definition }) => definition.id === retention.assetId);
|
|
78
|
+
return asset
|
|
79
|
+
? [
|
|
80
|
+
previewAsset(asset, input.from, `shared owner: ${retention.retainedBy.join(', ')}`),
|
|
81
|
+
]
|
|
82
|
+
: [];
|
|
83
|
+
}))
|
|
84
|
+
.filter((entry, index, entries) => entries.findIndex((candidate) => candidate.assetId === entry.assetId &&
|
|
85
|
+
candidate.path === entry.path) === index);
|
|
86
|
+
return {
|
|
87
|
+
pack: input.pack,
|
|
88
|
+
from: input.from,
|
|
89
|
+
to: input.to,
|
|
90
|
+
projectRoot: input.from === 'project' ? input.sourceRoot : input.destinationRoot,
|
|
91
|
+
sourceIntent: input.sourceInventory.intent.source,
|
|
92
|
+
status: conflicts.length > 0 ? 'blocked' : 'ready',
|
|
93
|
+
additions,
|
|
94
|
+
duplicates,
|
|
95
|
+
conflicts: conflicts.map((asset) => previewAsset(asset, input.to, 'newer-destination-asset')),
|
|
96
|
+
removals,
|
|
97
|
+
retained,
|
|
98
|
+
diagnostics: input.destinationInventory.intent.diagnostics,
|
|
99
|
+
destinationPlan,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export async function executeMigrationDestination(preview, destinationRoot, dependencies) {
|
|
103
|
+
if (preview.destinationPlan.pack !== preview.pack ||
|
|
104
|
+
preview.destinationPlan.scope !== preview.to ||
|
|
105
|
+
preview.destinationPlan.action !== 'migrate-destination') {
|
|
106
|
+
throw new Error('Migration destination plan does not match the preview');
|
|
107
|
+
}
|
|
108
|
+
if (preview.status === 'blocked' || preview.conflicts.length > 0) {
|
|
109
|
+
throw new Error(`Migration destination conflicts must be resolved before mutation: ${preview.conflicts.map(({ assetId }) => assetId).join(', ')}`);
|
|
110
|
+
}
|
|
111
|
+
const applied = await (dependencies.apply ?? applyPackReconcilePlan)(preview.destinationPlan, destinationRoot, { ...dependencies.applyDependencies, sync: undefined });
|
|
112
|
+
const destinationInventory = applied.inventory;
|
|
113
|
+
const drifted = destinationInventory.assets.filter(({ definition: asset, status }) => asset.ownership[preview.to] === 'managed' && status !== 'current');
|
|
114
|
+
if (destinationInventory.pack !== preview.pack ||
|
|
115
|
+
destinationInventory.scope !== preview.to ||
|
|
116
|
+
destinationInventory.completeness !== 'complete' ||
|
|
117
|
+
!destinationInventory.intent.enabled ||
|
|
118
|
+
destinationInventory.intent.source !== 'declared' ||
|
|
119
|
+
drifted.length > 0) {
|
|
120
|
+
throw new Error(`Migration destination verification did not produce a complete current declared installation for ${preview.pack} at ${preview.to} scope`);
|
|
121
|
+
}
|
|
122
|
+
const canonicalPaths = getPackDefinition(preview.pack)
|
|
123
|
+
.assets.filter(({ kind, scopes }) => scopes.includes(preview.to) && (kind === 'skill' || kind === 'agent'))
|
|
124
|
+
.map(({ destination }) => destination);
|
|
125
|
+
try {
|
|
126
|
+
await dependencies.sync?.({
|
|
127
|
+
scope: preview.to,
|
|
128
|
+
action: 'install',
|
|
129
|
+
canonicalPaths,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
const pendingSync = pendingSyncState(preview, preview.to, 'install', canonicalPaths);
|
|
134
|
+
return {
|
|
135
|
+
preview,
|
|
136
|
+
status: 'destination-sync-failed',
|
|
137
|
+
destinationInventory,
|
|
138
|
+
pendingSync,
|
|
139
|
+
recovery: [
|
|
140
|
+
`Destination provider sync failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
141
|
+
`Source was retained. Retry provider sync: ${pendingSync.command}`,
|
|
142
|
+
migrationRetry(preview),
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
return {
|
|
147
|
+
preview,
|
|
148
|
+
status: 'destination-verified',
|
|
149
|
+
destinationInventory,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
function migrationRetry(preview) {
|
|
153
|
+
return `Re-run interactively: ${renderCommand([
|
|
154
|
+
'oat',
|
|
155
|
+
'--cwd',
|
|
156
|
+
preview.projectRoot,
|
|
157
|
+
'tools',
|
|
158
|
+
'migrate',
|
|
159
|
+
'--pack',
|
|
160
|
+
preview.pack,
|
|
161
|
+
'--from',
|
|
162
|
+
preview.from,
|
|
163
|
+
'--to',
|
|
164
|
+
preview.to,
|
|
165
|
+
])}`;
|
|
166
|
+
}
|
|
167
|
+
function pendingSyncState(preview, scope, action, canonicalPaths) {
|
|
168
|
+
const flag = action === 'install' ? '--install-canonical' : '--remove-canonical';
|
|
169
|
+
return {
|
|
170
|
+
scope,
|
|
171
|
+
action,
|
|
172
|
+
projectRoot: preview.projectRoot,
|
|
173
|
+
canonicalPaths,
|
|
174
|
+
command: renderCommand([
|
|
175
|
+
'oat',
|
|
176
|
+
'--cwd',
|
|
177
|
+
preview.projectRoot,
|
|
178
|
+
'sync',
|
|
179
|
+
'--scope',
|
|
180
|
+
scope,
|
|
181
|
+
...canonicalPaths.flatMap((path) => [flag, path]),
|
|
182
|
+
]),
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function quoteCommandArgument(argument) {
|
|
186
|
+
if (/^[a-zA-Z0-9_@+=:,./-]+$/.test(argument))
|
|
187
|
+
return argument;
|
|
188
|
+
return process.platform === 'win32'
|
|
189
|
+
? `'${argument.replaceAll("'", "''")}'`
|
|
190
|
+
: `'${argument.replaceAll("'", `'"'"'`)}'`;
|
|
191
|
+
}
|
|
192
|
+
function renderCommand(arguments_) {
|
|
193
|
+
return arguments_.map(quoteCommandArgument).join(' ');
|
|
194
|
+
}
|
|
195
|
+
export async function completeMigrationSourceRemoval(destination, input, dependencies) {
|
|
196
|
+
if (destination.status !== 'destination-verified' &&
|
|
197
|
+
destination.status !== 'source-removal-failed' &&
|
|
198
|
+
destination.status !== 'source-sync-failed') {
|
|
199
|
+
throw new Error('Migration source removal requires a verified destination outcome');
|
|
200
|
+
}
|
|
201
|
+
if (!destination.destinationInventory) {
|
|
202
|
+
throw new Error('Migration source removal requires verified destination inventory');
|
|
203
|
+
}
|
|
204
|
+
const preview = destination.preview;
|
|
205
|
+
if (destination.status === 'source-sync-failed') {
|
|
206
|
+
if (!destination.pendingSync ||
|
|
207
|
+
destination.pendingSync.action !== 'remove') {
|
|
208
|
+
throw new Error('Source sync retry is missing its canonical path state');
|
|
209
|
+
}
|
|
210
|
+
try {
|
|
211
|
+
await dependencies.sync?.({
|
|
212
|
+
scope: destination.pendingSync.scope,
|
|
213
|
+
action: 'remove',
|
|
214
|
+
canonicalPaths: destination.pendingSync.canonicalPaths,
|
|
215
|
+
});
|
|
216
|
+
return {
|
|
217
|
+
...destination,
|
|
218
|
+
status: 'migrated',
|
|
219
|
+
pendingSync: undefined,
|
|
220
|
+
recovery: undefined,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
catch (error) {
|
|
224
|
+
return {
|
|
225
|
+
...destination,
|
|
226
|
+
recovery: [
|
|
227
|
+
`Source provider sync failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
228
|
+
`Retry provider sync: ${destination.pendingSync.command}`,
|
|
229
|
+
],
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (input.confirmation !== 'confirmed') {
|
|
234
|
+
return {
|
|
235
|
+
...destination,
|
|
236
|
+
status: 'retained-both',
|
|
237
|
+
recovery: [
|
|
238
|
+
input.confirmation === 'non-interactive'
|
|
239
|
+
? 'Source removal requires interactive confirmation; both scopes were retained.'
|
|
240
|
+
: 'Source removal was declined; both scopes were retained.',
|
|
241
|
+
migrationRetry(preview),
|
|
242
|
+
],
|
|
243
|
+
};
|
|
244
|
+
}
|
|
245
|
+
const sourceBefore = await dependencies.inventory();
|
|
246
|
+
assertInventory(sourceBefore, preview.pack, preview.from, 'source');
|
|
247
|
+
const sourceRetentions = (await dependencies.resolveSourceRetentions?.()) ?? [];
|
|
248
|
+
const sourcePlan = (dependencies.plan ?? planPackReconcile)({
|
|
249
|
+
pack: preview.pack,
|
|
250
|
+
scope: preview.from,
|
|
251
|
+
scopeRoot: input.sourceRoot,
|
|
252
|
+
assetsRoot: input.assetsRoot,
|
|
253
|
+
action: 'remove',
|
|
254
|
+
inventory: sourceBefore,
|
|
255
|
+
retainedAssets: sourceRetentions,
|
|
256
|
+
});
|
|
257
|
+
try {
|
|
258
|
+
const applied = await (dependencies.apply ?? applyPackReconcilePlan)(sourcePlan, input.sourceRoot, {
|
|
259
|
+
...dependencies.applyDependencies,
|
|
260
|
+
inventory: dependencies.inventory,
|
|
261
|
+
sync: undefined,
|
|
262
|
+
});
|
|
263
|
+
try {
|
|
264
|
+
await dependencies.sync?.({
|
|
265
|
+
scope: preview.from,
|
|
266
|
+
action: 'remove',
|
|
267
|
+
canonicalPaths: sourcePlan.changedCanonicalPaths,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
catch (error) {
|
|
271
|
+
const pendingSync = pendingSyncState(preview, preview.from, 'remove', sourcePlan.changedCanonicalPaths);
|
|
272
|
+
return {
|
|
273
|
+
preview,
|
|
274
|
+
status: 'source-sync-failed',
|
|
275
|
+
destinationInventory: destination.destinationInventory,
|
|
276
|
+
sourceInventory: applied.inventory,
|
|
277
|
+
pendingSync,
|
|
278
|
+
recovery: [
|
|
279
|
+
`Source provider sync failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
280
|
+
`Retry provider sync: ${pendingSync.command}`,
|
|
281
|
+
],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return {
|
|
285
|
+
preview,
|
|
286
|
+
status: 'migrated',
|
|
287
|
+
destinationInventory: destination.destinationInventory,
|
|
288
|
+
sourceInventory: applied.inventory,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
catch (error) {
|
|
292
|
+
let sourceInventory = sourceBefore;
|
|
293
|
+
let inventoryFailure = null;
|
|
294
|
+
try {
|
|
295
|
+
sourceInventory = await dependencies.inventory();
|
|
296
|
+
}
|
|
297
|
+
catch (inventoryError) {
|
|
298
|
+
inventoryFailure =
|
|
299
|
+
inventoryError instanceof Error
|
|
300
|
+
? inventoryError.message
|
|
301
|
+
: String(inventoryError);
|
|
302
|
+
}
|
|
303
|
+
const remaining = sourceInventory.assets
|
|
304
|
+
.filter(({ definition: asset, status }) => asset.ownership[preview.from] === 'managed' &&
|
|
305
|
+
status !== 'missing' &&
|
|
306
|
+
!sourceRetentions.some(({ assetId }) => assetId === asset.id))
|
|
307
|
+
.map(({ path }) => path);
|
|
308
|
+
const detail = error instanceof Error ? error.message : String(error);
|
|
309
|
+
return {
|
|
310
|
+
preview,
|
|
311
|
+
status: 'source-removal-failed',
|
|
312
|
+
destinationInventory: destination.destinationInventory,
|
|
313
|
+
sourceInventory,
|
|
314
|
+
recovery: [
|
|
315
|
+
`Source removal failed: ${detail}`,
|
|
316
|
+
`Remaining source paths: ${remaining.length > 0 ? remaining.join(', ') : 'inventory unavailable'}`,
|
|
317
|
+
...(inventoryFailure
|
|
318
|
+
? [`Source re-inventory failed: ${inventoryFailure}`]
|
|
319
|
+
: []),
|
|
320
|
+
migrationRetry(preview),
|
|
321
|
+
],
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
}
|
|
@@ -1,13 +1,24 @@
|
|
|
1
1
|
import type { CommandContext } from '../../../app/command-context.js';
|
|
2
|
+
import { type InventoryScopedPackInput, type ScopedPackInventory } from '../../tools/shared/pack-inventory.js';
|
|
2
3
|
import type { ScanToolsOptions } from '../../tools/shared/scan-tools.js';
|
|
3
4
|
import type { ToolInfo } from '../../tools/shared/types.js';
|
|
4
5
|
export interface OutdatedToolsDependencies {
|
|
5
6
|
scanTools: (options: ScanToolsOptions) => Promise<ToolInfo[]>;
|
|
6
7
|
resolveScopeRoot: (scope: 'project' | 'user', cwd: string, home: string) => Promise<string>;
|
|
7
8
|
resolveAssetsRoot: () => Promise<string>;
|
|
9
|
+
inventoryScopedPack?: (input: InventoryScopedPackInput) => Promise<ScopedPackInventory>;
|
|
10
|
+
}
|
|
11
|
+
export interface RepairablePack {
|
|
12
|
+
pack: ScopedPackInventory['pack'];
|
|
13
|
+
scope: ScopedPackInventory['scope'];
|
|
14
|
+
completeness: ScopedPackInventory['completeness'];
|
|
15
|
+
missing: string[];
|
|
16
|
+
drifted: string[];
|
|
17
|
+
intended: boolean;
|
|
8
18
|
}
|
|
9
19
|
export interface OutdatedToolsResult {
|
|
10
20
|
tools: ToolInfo[];
|
|
21
|
+
packs: RepairablePack[];
|
|
11
22
|
}
|
|
12
23
|
export declare function runOutdatedTools(context: CommandContext, dependencies: OutdatedToolsDependencies): Promise<OutdatedToolsResult>;
|
|
13
24
|
//# sourceMappingURL=outdated-tools.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"outdated-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/outdated/outdated-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,gBAAgB,EAAE,CAChB,KAAK,EAAE,SAAS,GAAG,MAAM,EACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"outdated-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/outdated/outdated-tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAE3D,OAAO,EAEL,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACzB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAE7D,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,gBAAgB,EAAE,CAChB,KAAK,EAAE,SAAS,GAAG,MAAM,EACzB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,mBAAmB,CAAC,EAAE,CACpB,KAAK,EAAE,wBAAwB,KAC5B,OAAO,CAAC,mBAAmB,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAClC,KAAK,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;IACpC,YAAY,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAC;IAClD,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,mBAAmB;IAClC,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,wBAAsB,gBAAgB,CACpC,OAAO,EAAE,cAAc,EACvB,YAAY,EAAE,yBAAyB,GACtC,OAAO,CAAC,mBAAmB,CAAC,CAoG9B"}
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { resolveConcreteScopes } from '../../shared/shared.utils.js';
|
|
2
|
+
import { inventoryScopedPack, } from '../../tools/shared/pack-inventory.js';
|
|
3
|
+
import { PACK_MANIFEST } from '../../tools/shared/pack-manifest.js';
|
|
2
4
|
export async function runOutdatedTools(context, dependencies) {
|
|
3
5
|
const { logger } = context;
|
|
4
6
|
const scopes = resolveConcreteScopes(context.scope);
|
|
5
7
|
const assetsRoot = await dependencies.resolveAssetsRoot();
|
|
6
8
|
const outdated = [];
|
|
9
|
+
const packs = [];
|
|
7
10
|
for (const scope of scopes) {
|
|
8
11
|
const scopeRoot = await dependencies.resolveScopeRoot(scope, context.cwd, context.home);
|
|
9
12
|
const tools = await dependencies.scanTools({
|
|
@@ -12,14 +15,36 @@ export async function runOutdatedTools(context, dependencies) {
|
|
|
12
15
|
assetsRoot,
|
|
13
16
|
});
|
|
14
17
|
outdated.push(...tools.filter((t) => t.status === 'outdated'));
|
|
18
|
+
for (const { name: pack, allowedScopes } of PACK_MANIFEST) {
|
|
19
|
+
if (!allowedScopes.includes(scope))
|
|
20
|
+
continue;
|
|
21
|
+
const inventory = await (dependencies.inventoryScopedPack ?? inventoryScopedPack)({ pack, scope, scopeRoot, assetsRoot });
|
|
22
|
+
const missing = inventory.assets
|
|
23
|
+
.filter(({ definition, status }) => definition.ownership[scope] === 'managed' && status === 'missing')
|
|
24
|
+
.map(({ definition }) => definition.id);
|
|
25
|
+
const drifted = inventory.assets
|
|
26
|
+
.filter(({ status }) => status === 'outdated' || status === 'newer')
|
|
27
|
+
.map(({ definition }) => definition.id);
|
|
28
|
+
if (drifted.length > 0 ||
|
|
29
|
+
(inventory.intent.enabled && inventory.completeness !== 'complete')) {
|
|
30
|
+
packs.push({
|
|
31
|
+
pack,
|
|
32
|
+
scope,
|
|
33
|
+
completeness: inventory.completeness,
|
|
34
|
+
missing,
|
|
35
|
+
drifted,
|
|
36
|
+
intended: inventory.intent.enabled,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}
|
|
15
40
|
}
|
|
16
41
|
if (context.json) {
|
|
17
|
-
logger.json({ tools: outdated });
|
|
18
|
-
return { tools: outdated };
|
|
42
|
+
logger.json({ tools: outdated, packs });
|
|
43
|
+
return { tools: outdated, packs };
|
|
19
44
|
}
|
|
20
|
-
if (outdated.length === 0) {
|
|
45
|
+
if (outdated.length === 0 && packs.length === 0) {
|
|
21
46
|
logger.info('All tools are up to date.');
|
|
22
|
-
return { tools: outdated };
|
|
47
|
+
return { tools: outdated, packs };
|
|
23
48
|
}
|
|
24
49
|
logger.info('Outdated tools:\n');
|
|
25
50
|
const header = formatRow('NAME', 'TYPE', 'INSTALLED', 'AVAILABLE', 'PACK', 'SCOPE');
|
|
@@ -29,7 +54,10 @@ export async function runOutdatedTools(context, dependencies) {
|
|
|
29
54
|
for (const tool of outdated) {
|
|
30
55
|
logger.info(formatRow(tool.name, tool.type, tool.version ?? '-', tool.bundledVersion ?? '-', tool.pack, tool.scope));
|
|
31
56
|
}
|
|
32
|
-
|
|
57
|
+
for (const pack of packs) {
|
|
58
|
+
logger.info(`Pack ${pack.pack}@${pack.scope} is repairable (${pack.completeness}; missing=${pack.missing.length}; drifted=${pack.drifted.length}).`);
|
|
59
|
+
}
|
|
60
|
+
return { tools: outdated, packs };
|
|
33
61
|
}
|
|
34
62
|
function formatRow(name, type, installed, available, pack, scope) {
|
|
35
63
|
return [
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/index.ts"],"names":[],"mappings":"AAUA,OAAO,EACL,KAAK,oBAAoB,EAE1B,MAAM,kCAAkC,CAAC;AAS1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,OAAO,EAEL,KAAK,uBAAuB,EAE7B,MAAM,gBAAgB,CAAC;AAmExB,wBAAgB,wBAAwB,CACtC,YAAY,GAAE,uBAA6C,EAC3D,gBAAgB,GAAE,oBAA8C,GAC/D,OAAO,CAiHT"}
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { execFile } from 'node:child_process';
|
|
2
|
-
import { rm } from 'node:fs/promises';
|
|
2
|
+
import { lstat, rm } from 'node:fs/promises';
|
|
3
|
+
import { relative } from 'node:path';
|
|
3
4
|
import { buildCommandContext } from '../../../app/command-context.js';
|
|
4
5
|
import { withScopeOption } from '../../shared/scope-option.js';
|
|
5
6
|
import { readGlobalOptions, resolveConcreteScopes, } from '../../shared/shared.utils.js';
|
|
6
7
|
import { autoSync, } from '../../tools/shared/auto-sync.js';
|
|
7
|
-
import { reconcileProjectToolsConfig } from '../../tools/shared/project-tools-config.js';
|
|
8
8
|
import { scanTools } from '../../tools/shared/scan-tools.js';
|
|
9
|
-
import {
|
|
9
|
+
import { hasScopedPackOwnershipEvidence, writeScopedPackIntent, } from '../../tools/shared/scoped-pack-intent.js';
|
|
10
10
|
import { resolveAssetsRoot } from '../../../fs/assets.js';
|
|
11
11
|
import { resolveProjectRoot, resolveScopeRoot } from '../../../fs/paths.js';
|
|
12
12
|
import { Command } from 'commander';
|
|
@@ -25,11 +25,37 @@ const defaultDependencies = {
|
|
|
25
25
|
removeFile: async (path) => {
|
|
26
26
|
await rm(path, { force: true });
|
|
27
27
|
},
|
|
28
|
+
pathExists: async (path) => {
|
|
29
|
+
try {
|
|
30
|
+
await lstat(path);
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
catch (error) {
|
|
34
|
+
if (typeof error === 'object' &&
|
|
35
|
+
error !== null &&
|
|
36
|
+
'code' in error &&
|
|
37
|
+
error.code === 'ENOENT') {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
},
|
|
43
|
+
hasPackOwnershipEvidence: async (pack, scope, scopeRoot) => hasScopedPackOwnershipEvidence({ pack, scope, scopeRoot }),
|
|
28
44
|
};
|
|
29
45
|
const defaultSyncDependencies = {
|
|
30
|
-
runSync: async ({ scope, cwd }) => {
|
|
46
|
+
runSync: async ({ scope, cwd, removedCanonicalPaths }) => {
|
|
47
|
+
const args = [
|
|
48
|
+
...process.execArgv,
|
|
49
|
+
process.argv[1],
|
|
50
|
+
'sync',
|
|
51
|
+
'--scope',
|
|
52
|
+
scope,
|
|
53
|
+
];
|
|
54
|
+
for (const canonicalPath of removedCanonicalPaths ?? []) {
|
|
55
|
+
args.push('--remove-canonical', canonicalPath);
|
|
56
|
+
}
|
|
31
57
|
await new Promise((resolve, reject) => {
|
|
32
|
-
execFile(process.execPath,
|
|
58
|
+
execFile(process.execPath, args, { cwd }, (error) => {
|
|
33
59
|
if (error)
|
|
34
60
|
reject(error);
|
|
35
61
|
else
|
|
@@ -69,18 +95,28 @@ export function createToolsRemoveCommand(dependencies = defaultDependencies, syn
|
|
|
69
95
|
const scopes = resolveConcreteScopes(context.scope);
|
|
70
96
|
const dryRun = opts.dryRun ?? false;
|
|
71
97
|
const result = await removeTools(target, scopes, context.cwd, context.home, dryRun, dependencies);
|
|
72
|
-
if (!dryRun &&
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
98
|
+
if (!dryRun && target.kind !== 'name') {
|
|
99
|
+
const packs = target.kind === 'pack' ? [target.pack] : [...VALID_PACKS];
|
|
100
|
+
for (const scope of scopes) {
|
|
101
|
+
const scopeRoot = await dependencies.resolveScopeRoot(scope, context.cwd, context.home);
|
|
102
|
+
for (const pack of packs) {
|
|
103
|
+
// Durable scoped intent is what lets `oat tools update` restore a
|
|
104
|
+
// pack whose files are all missing, so it may only be cleared for a
|
|
105
|
+
// pack this run actually removed something for. A removal that
|
|
106
|
+
// removed nothing reports nothing and must leave intent alone.
|
|
107
|
+
const removedForPack = result.packOutcomes.some((outcome) => outcome.pack === pack &&
|
|
108
|
+
outcome.scope === scope &&
|
|
109
|
+
outcome.removed);
|
|
110
|
+
if (!removedForPack)
|
|
111
|
+
continue;
|
|
112
|
+
await writeScopedPackIntent({
|
|
113
|
+
pack,
|
|
114
|
+
scope,
|
|
115
|
+
scopeRoot,
|
|
116
|
+
enabled: false,
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
84
120
|
}
|
|
85
121
|
if (result.notInstalled.length > 0) {
|
|
86
122
|
if (context.json) {
|
|
@@ -93,9 +129,14 @@ export function createToolsRemoveCommand(dependencies = defaultDependencies, syn
|
|
|
93
129
|
return;
|
|
94
130
|
}
|
|
95
131
|
// Auto-sync after mutations (before output so sync errors are captured)
|
|
96
|
-
if (
|
|
97
|
-
const
|
|
98
|
-
|
|
132
|
+
if (!dryRun && opts.sync !== false) {
|
|
133
|
+
for (const scope of scopes) {
|
|
134
|
+
const scopeRoot = await dependencies.resolveScopeRoot(scope, context.cwd, context.home);
|
|
135
|
+
const removedCanonicalPaths = canonicalRemovalPaths(result, scope, scopeRoot);
|
|
136
|
+
if (removedCanonicalPaths.length === 0)
|
|
137
|
+
continue;
|
|
138
|
+
await autoSync([scope], context.cwd, context.home, logger, syncDependencies, { removedCanonicalPaths });
|
|
139
|
+
}
|
|
99
140
|
}
|
|
100
141
|
if (context.json) {
|
|
101
142
|
logger.json({ target: describeTarget(target), dryRun, result });
|
|
@@ -112,6 +153,18 @@ export function createToolsRemoveCommand(dependencies = defaultDependencies, syn
|
|
|
112
153
|
}
|
|
113
154
|
});
|
|
114
155
|
}
|
|
156
|
+
function canonicalRemovalPaths(result, scope, scopeRoot) {
|
|
157
|
+
const paths = result.removedAssets
|
|
158
|
+
.filter((asset) => asset.scope === scope)
|
|
159
|
+
.map((asset) => relative(scopeRoot, asset.path).replaceAll('\\', '/'))
|
|
160
|
+
.filter((path) => /^\.agents\/(?:skills\/[^/]+|agents\/[^/]+\.md)$/.test(path));
|
|
161
|
+
paths.push(...result.removed
|
|
162
|
+
.filter((tool) => tool.scope === scope)
|
|
163
|
+
.map((tool) => tool.type === 'skill'
|
|
164
|
+
? `.agents/skills/${tool.name}`
|
|
165
|
+
: `.agents/agents/${tool.name}.md`));
|
|
166
|
+
return [...new Set(paths)];
|
|
167
|
+
}
|
|
115
168
|
function resolveTarget(name, pack, all) {
|
|
116
169
|
const specified = [name, pack, all].filter(Boolean).length;
|
|
117
170
|
if (specified !== 1)
|
|
@@ -16,14 +16,42 @@ export interface RemoveToolsDependencies {
|
|
|
16
16
|
resolveAssetsRoot: () => Promise<string>;
|
|
17
17
|
removeDirectory: (path: string) => Promise<void>;
|
|
18
18
|
removeFile: (path: string) => Promise<void>;
|
|
19
|
+
pathExists: (path: string) => Promise<boolean>;
|
|
20
|
+
hasPackOwnershipEvidence: (pack: PackName, scope: ConcreteScope, scopeRoot: string) => Promise<boolean>;
|
|
19
21
|
}
|
|
20
22
|
interface RemovedTool {
|
|
21
23
|
name: string;
|
|
22
24
|
type: 'skill' | 'agent';
|
|
23
25
|
scope: ConcreteScope;
|
|
24
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Per-pack, per-scope evidence that a removal actually acted on the pack.
|
|
29
|
+
*
|
|
30
|
+
* `removed` is true when a scanned pack tool matched, or when a
|
|
31
|
+
* manifest-declared managed destination for that pack existed on disk before
|
|
32
|
+
* removal ran (including one retained for a shared owner). It gates clearing
|
|
33
|
+
* durable scoped intent: a removal that found no trace of a pack removed
|
|
34
|
+
* nothing, reports that nothing was removed, and must leave the intent that
|
|
35
|
+
* `oat tools update` restores from (FR5) untouched. Under `--dry-run` it
|
|
36
|
+
* reports what a real run would have acted on.
|
|
37
|
+
*/
|
|
38
|
+
export interface PackRemovalOutcome {
|
|
39
|
+
pack: PackName;
|
|
40
|
+
scope: ConcreteScope;
|
|
41
|
+
removed: boolean;
|
|
42
|
+
}
|
|
25
43
|
export interface RemoveResult {
|
|
26
44
|
removed: RemovedTool[];
|
|
45
|
+
removedAssets: Array<{
|
|
46
|
+
path: string;
|
|
47
|
+
scope: ConcreteScope;
|
|
48
|
+
}>;
|
|
49
|
+
retainedOwnerData: Array<{
|
|
50
|
+
path: string;
|
|
51
|
+
scope: ConcreteScope;
|
|
52
|
+
reason: string;
|
|
53
|
+
}>;
|
|
54
|
+
packOutcomes: PackRemovalOutcome[];
|
|
27
55
|
notInstalled: string[];
|
|
28
56
|
}
|
|
29
57
|
export declare function removeTools(target: RemoveTarget, scopes: ConcreteScope[], cwd: string, home: string, dryRun: boolean, deps: RemoveToolsDependencies): Promise<RemoveResult>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"remove-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/remove-tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"remove-tools.d.ts","sourceRoot":"","sources":["../../../../src/commands/tools/remove/remove-tools.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mCAAmC,CAAC;AAC1E,OAAO,KAAK,EAEV,QAAQ,EACR,QAAQ,EACT,MAAM,8BAA8B,CAAC;AAMtC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAEnD,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAC9B;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,KAAK,CAAA;CAAE,CAAC;AAEpB,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;IAC9D,gBAAgB,EAAE,CAChB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,KACT,OAAO,CAAC,MAAM,CAAC,CAAC;IACrB,iBAAiB,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAC;IACzC,eAAe,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACjD,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,wBAAwB,EAAE,CACxB,IAAI,EAAE,QAAQ,EACd,KAAK,EAAE,aAAa,EACpB,SAAS,EAAE,MAAM,KACd,OAAO,CAAC,OAAO,CAAC,CAAC;CACvB;AAED,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,GAAG,OAAO,CAAC;IACxB,KAAK,EAAE,aAAa,CAAC;CACtB;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,WAAW,EAAE,CAAC;IACvB,aAAa,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,aAAa,CAAA;KAAE,CAAC,CAAC;IAC7D,iBAAiB,EAAE,KAAK,CAAC;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,aAAa,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC;KAChB,CAAC,CAAC;IACH,YAAY,EAAE,kBAAkB,EAAE,CAAC;IACnC,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA8MD,wBAAsB,WAAW,CAC/B,MAAM,EAAE,YAAY,EACpB,MAAM,EAAE,aAAa,EAAE,EACvB,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,OAAO,EACf,IAAI,EAAE,uBAAuB,GAC5B,OAAO,CAAC,YAAY,CAAC,CAsGvB"}
|