@agentxm/extension-lifecycle 0.29.4 → 0.30.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/activation/set-activation.d.ts +22 -40
- package/dist/src/activation/set-activation.js +191 -336
- package/dist/src/demote/demote-to-external-source.d.ts +1 -1
- package/dist/src/demote/demote-to-external-source.js +53 -24
- package/dist/src/hooks/install/plan.js +6 -3
- package/dist/src/hooks/uninstall/plan.js +4 -3
- package/dist/src/index.d.ts +1 -5
- package/dist/src/index.js +1 -4
- package/dist/src/install/install-extensions.d.ts +5 -5
- package/dist/src/install/install-extensions.js +2 -1
- package/dist/src/install/root-intent.d.ts +2 -2
- package/dist/src/install/vocabulary.d.ts +5 -4
- package/dist/src/knowledge/install/plan.js +2 -1
- package/dist/src/knowledge/uninstall/plan.js +4 -3
- package/dist/src/mcps/install/plan.d.ts +1 -1
- package/dist/src/mcps/install/plan.js +59 -3
- package/dist/src/mcps/operations/uninstall.js +2 -1
- package/dist/src/mcps/uninstall/plan.js +13 -4
- package/dist/src/packs/graph-transition.d.ts +0 -25
- package/dist/src/packs/graph-transition.js +0 -101
- package/dist/src/packs/install/plan.d.ts +0 -13
- package/dist/src/packs/install/plan.js +12 -9
- package/dist/src/packs/member-install-step.js +8 -21
- package/dist/src/packs/promote-authored-pack.js +6 -3
- package/dist/src/packs/uninstall/plan.js +53 -21
- package/dist/src/rules/install/plan.js +6 -3
- package/dist/src/rules/uninstall/plan.js +4 -3
- package/dist/src/skills/application/index.d.ts +42 -0
- package/dist/src/skills/application/index.js +37 -0
- package/dist/src/skills/domain/selection.d.ts +22 -0
- package/dist/src/skills/domain/selection.js +18 -0
- package/dist/src/skills/install/artifact.d.ts +4 -0
- package/dist/src/skills/install/artifact.js +29 -0
- package/dist/src/skills/install/bundled.js +4 -4
- package/dist/src/skills/install/plan.d.ts +15 -4
- package/dist/src/skills/install/plan.js +114 -97
- package/dist/src/skills/uninstall/plan.js +13 -5
- package/dist/src/step-failure.js +12 -0
- package/dist/src/subagents/application/index.d.ts +42 -0
- package/dist/src/subagents/application/index.js +37 -0
- package/dist/src/subagents/domain/selection.d.ts +22 -0
- package/dist/src/subagents/domain/selection.js +18 -0
- package/dist/src/subagents/install/plan.d.ts +2 -2
- package/dist/src/subagents/install/plan.js +4 -4
- package/dist/src/subagents/operations/enable.js +1 -3
- package/dist/src/subagents/uninstall/plan.js +5 -4
- package/dist/src/testing.d.ts +3 -2
- package/dist/src/testing.js +9 -6
- package/dist/src/uninstall/root-intent.d.ts +2 -1
- package/dist/src/uninstall/root-intent.js +1 -0
- package/dist/src/uninstall/undesired-target.d.ts +26 -0
- package/dist/src/uninstall/undesired-target.js +56 -0
- package/dist/src/uninstall/uninstall-extensions.d.ts +1 -1
- package/dist/src/uninstall/uninstall-extensions.js +103 -3
- package/dist/src/update/root-request.d.ts +2 -2
- package/dist/src/update/selective/plan.d.ts +2 -3
- package/dist/src/update/selective/plan.js +2 -2
- package/dist/src/update/selective/requirements.d.ts +2 -6
- package/dist/src/update/selective/requirements.js +1 -3
- package/dist/src/update/selective/selection.js +1 -1
- package/dist/src/update/selective/skills.d.ts +2 -4
- package/dist/src/update/selective/skills.js +22 -27
- package/dist/src/update/selective/subagents.d.ts +2 -3
- package/dist/src/update/selective/subagents.js +11 -9
- package/dist/src/update/selective/use-case.d.ts +7 -7
- package/dist/src/update/selective/vocabulary.d.ts +1 -1
- package/dist/src/update/selector.js +1 -1
- package/dist/src/update/targeted-plan.js +4 -2
- package/dist/src/workspace-paths.d.ts +0 -6
- package/dist/src/workspace-paths.js +0 -10
- package/package.json +30 -24
- package/dist/src/glob.d.ts +0 -35
- package/dist/src/glob.js +0 -75
- package/dist/src/install/selection-interaction.d.ts +0 -46
- package/dist/src/install/selection-interaction.js +0 -22
- package/dist/src/skills/install/selection.d.ts +0 -26
- package/dist/src/skills/install/selection.js +0 -41
- package/dist/src/skills/operations/install-result.d.ts +0 -33
- package/dist/src/skills/operations/install-result.js +0 -10
- package/dist/src/skills/operations/install.d.ts +0 -67
- package/dist/src/skills/operations/install.js +0 -591
- package/dist/src/subagents/install/selection.d.ts +0 -26
- package/dist/src/subagents/install/selection.js +0 -37
- package/dist/src/uninstall/retention-policy.d.ts +0 -21
- package/dist/src/uninstall/retention-policy.js +0 -28
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import * as Option from "effect/Option";
|
|
2
|
+
const gitHostedSourceOrigin = (ref) => {
|
|
3
|
+
const source = ref.source;
|
|
4
|
+
switch (source.type) {
|
|
5
|
+
case "github":
|
|
6
|
+
case "gitlab":
|
|
7
|
+
case "bitbucket":
|
|
8
|
+
return `${source.url.origin}/${source.owner}/${source.repo}`;
|
|
9
|
+
case "azurerepos":
|
|
10
|
+
return `${source.url.origin}/${source.organization}/${source.project}/_git/${source.repo}`;
|
|
11
|
+
case "git":
|
|
12
|
+
return source.url.href;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const gitHostedSkillArtifactSource = (ref) => {
|
|
16
|
+
if (ref.refType !== "git-hosted")
|
|
17
|
+
return undefined;
|
|
18
|
+
const gitTreeHash = ref.gitTreeSha;
|
|
19
|
+
const gitRef = Option.getOrUndefined(ref.source.ref);
|
|
20
|
+
const directory = ref.sourcePath === undefined || ref.sourcePath.length === 0 ? "." : ref.sourcePath;
|
|
21
|
+
return {
|
|
22
|
+
type: ref.source.type,
|
|
23
|
+
origin: gitHostedSourceOrigin(ref),
|
|
24
|
+
...(gitRef !== undefined ? { ref: gitRef } : {}),
|
|
25
|
+
directory,
|
|
26
|
+
gitTreeHash,
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
//# sourceMappingURL=artifact.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { renderAxmSkillRecovery } from "@agentxm/cli-maintenance/official-skill/adapters/cli";
|
|
1
2
|
/**
|
|
2
3
|
* Installing the official AXM skill that ships inside the CLI.
|
|
3
4
|
*
|
|
@@ -16,7 +17,7 @@ import * as FileSystem from "effect/FileSystem";
|
|
|
16
17
|
import * as Option from "effect/Option";
|
|
17
18
|
import * as Path from "effect/Path";
|
|
18
19
|
import { ensureSkillAgentArtifact, replaceCanonicalDirectory, } from "@agentxm/extension-materialization";
|
|
19
|
-
import { AXM_SKILL_CLI_VERSION_METADATA_KEY, AXM_SKILL_CLI_VERSION_RANGE_METADATA_KEY, AXM_SKILL_FQN, evaluateAxmSkillCompatibility, } from "@agentxm/
|
|
20
|
+
import { AXM_SKILL_CLI_VERSION_METADATA_KEY, AXM_SKILL_CLI_VERSION_RANGE_METADATA_KEY, AXM_SKILL_FQN, evaluateAxmSkillCompatibility, } from "@agentxm/cli-maintenance/official-skill/domain";
|
|
20
21
|
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
21
22
|
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
22
23
|
import { WorkspaceMutations, sanitizeName } from "@agentxm/workspace-state";
|
|
@@ -173,13 +174,12 @@ export const installBundledAxmSkill = Effect.gen(function* () {
|
|
|
173
174
|
},
|
|
174
175
|
});
|
|
175
176
|
if (compatibility.status === "incompatible") {
|
|
177
|
+
const recovery = renderAxmSkillRecovery(compatibility.recovery);
|
|
176
178
|
return yield* installRefused({
|
|
177
179
|
category: "internal",
|
|
178
180
|
detail: compatibility.detail ??
|
|
179
181
|
"Bundled AXM skill remained incompatible after workspace installation",
|
|
180
|
-
...(
|
|
181
|
-
? {}
|
|
182
|
-
: { cmd: compatibility.recovery.nextAction }),
|
|
182
|
+
...(recovery.nextAction === null ? {} : { cmd: recovery.nextAction }),
|
|
183
183
|
});
|
|
184
184
|
}
|
|
185
185
|
}),
|
|
@@ -21,12 +21,11 @@ import { type PackageUrlParts } from "@agentxm/extension-model/unstable/packagin
|
|
|
21
21
|
import type { Source } from "@agentxm/extension-model/unstable/sources/types";
|
|
22
22
|
import type { VersionRange } from "@agentxm/extension-model/unstable/version-constraints";
|
|
23
23
|
import { type SourceResolutionFailure } from "@agentxm/extension-sources";
|
|
24
|
-
import { type Plan } from "@agentxm/workspace-operations";
|
|
24
|
+
import { type Plan, type PlannedJobStep } from "@agentxm/workspace-operations";
|
|
25
25
|
import type { ExtensionLifecycleFailed } from "../../errors.js";
|
|
26
26
|
import type { RegistryLookupProbe } from "../../install/registry-source-resolution.js";
|
|
27
|
-
import type { ExtensionSelectionCancelled } from "../../install/selection-interaction.js";
|
|
28
|
-
import { ExtensionSelectionInteraction } from "../../install/selection-interaction.js";
|
|
29
27
|
import { type InstallStepRequirements, type ResolveInstallRequirements, type SkillInstallIntent } from "../../install/vocabulary.js";
|
|
28
|
+
import { SkillSelectionInteraction, type SkillSelectionFailure } from "../application/index.js";
|
|
30
29
|
/** A skill source after grammar parsing, before anything is discovered. */
|
|
31
30
|
export interface ParsedSkillInstallRequest {
|
|
32
31
|
readonly source: Source;
|
|
@@ -66,7 +65,19 @@ export declare const parseSkillInstallRequest: (args: SkillInstallArgs) => Effec
|
|
|
66
65
|
/** Discover the skills the parsed source offers, or refuse when it has none. */
|
|
67
66
|
export declare const discoverSkillRefs: (request: ParsedSkillInstallRequest) => Effect.Effect<ReadonlyArray<SkillExtensionRef>, ExtensionLifecycleFailed, ResolveInstallRequirements>;
|
|
68
67
|
/** Settle which of the discovered skills this request installs. */
|
|
69
|
-
export declare const finalizeSkillInstallIntent: (request: ParsedSkillInstallRequest, discovered: ReadonlyArray<SkillExtensionRef>) => Effect.Effect<SkillInstallIntent, ExtensionLifecycleFailed |
|
|
68
|
+
export declare const finalizeSkillInstallIntent: (request: ParsedSkillInstallRequest, discovered: ReadonlyArray<SkillExtensionRef>) => Effect.Effect<SkillInstallIntent, ExtensionLifecycleFailed | SkillSelectionFailure, SkillSelectionInteraction>;
|
|
69
|
+
/** Installing declares intent; an update advances the resolution of existing intent. */
|
|
70
|
+
export type SkillInstallationStepInput = {
|
|
71
|
+
readonly ref: SkillExtensionRef;
|
|
72
|
+
readonly force: boolean;
|
|
73
|
+
} & ({
|
|
74
|
+
readonly operation: "install";
|
|
75
|
+
readonly versionRange: Option.Option<VersionRange>;
|
|
76
|
+
} | {
|
|
77
|
+
readonly operation: "update";
|
|
78
|
+
});
|
|
79
|
+
/** One skill-owned application step, shared by install and selective update. */
|
|
80
|
+
export declare const planSkillInstallationStep: (input: SkillInstallationStepInput) => Effect.Effect<PlannedJobStep<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SkillManager | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient>;
|
|
70
81
|
/** The closures a settled skill intent becomes. */
|
|
71
82
|
export declare const planSkillInstall: (intent: SkillInstallIntent) => Effect.Effect<Plan<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SkillManager | FileSystem.FileSystem | Path.Path | HttpClient.HttpClient>;
|
|
72
83
|
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -17,7 +17,8 @@ import * as Option from "effect/Option";
|
|
|
17
17
|
import * as Path from "effect/Path";
|
|
18
18
|
import * as Result from "effect/Result";
|
|
19
19
|
import * as Schema from "effect/Schema";
|
|
20
|
-
import { SkillManager, artifactAgentIdsFromTargets, artifactTargetAgentIds,
|
|
20
|
+
import { SkillManager, artifactAgentIdsFromTargets, artifactTargetAgentIds, computeSkillSourceHash, groupInstallTargetsByDirectory, } from "@agentxm/extension-materialization";
|
|
21
|
+
import { buildInstallOperation } from "@agentxm/workspace-reconciliation";
|
|
21
22
|
import { matchesReleaseAgeExcludePattern, } from "@agentxm/extension-model/unstable/extensions";
|
|
22
23
|
import { PackageUrlPartsSchema, formatPackageDisplay, } from "@agentxm/extension-model/unstable/packaging";
|
|
23
24
|
import { parseInputPattern, } from "@agentxm/extension-model/unstable/sources/parser";
|
|
@@ -27,11 +28,10 @@ import { createRegistryClient } from "@agentxm/registry-client";
|
|
|
27
28
|
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
28
29
|
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
29
30
|
import { WorkspaceMutations, sanitizeName } from "@agentxm/workspace-state";
|
|
30
|
-
import { gitHostedSkillArtifactSource } from "
|
|
31
|
+
import { gitHostedSkillArtifactSource } from "./artifact.js";
|
|
31
32
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
32
|
-
import { ExtensionSelectionInteraction } from "../../install/selection-interaction.js";
|
|
33
33
|
import { installRefused, } from "../../install/vocabulary.js";
|
|
34
|
-
import { determineSkillsToInstall } from "
|
|
34
|
+
import { determineSkillsToInstall, SkillSelectionInteraction, } from "../application/index.js";
|
|
35
35
|
import { resolveSkillInstallSource } from "./source.js";
|
|
36
36
|
const noSkillsFoundHowToFix = (source) => {
|
|
37
37
|
if (source.type === "registry") {
|
|
@@ -316,104 +316,121 @@ const targetChangeBeforeInstall = (args) => Effect.gen(function* () {
|
|
|
316
316
|
const exists = yield* fs.exists(args.linkPath).pipe(Effect.catch(() => Effect.succeed(false)));
|
|
317
317
|
return exists ? "updated" : "created";
|
|
318
318
|
});
|
|
319
|
-
/**
|
|
320
|
-
export const
|
|
319
|
+
/** One skill-owned application step, shared by install and selective update. */
|
|
320
|
+
export const planSkillInstallationStep = (input) => Effect.gen(function* () {
|
|
321
321
|
const ws = yield* WorkspaceMutations;
|
|
322
322
|
const skillManager = yield* SkillManager;
|
|
323
323
|
const agentRepo = yield* CodingAgentRepository;
|
|
324
324
|
const fs = yield* FileSystem.FileSystem;
|
|
325
325
|
const path = yield* Path.Path;
|
|
326
|
-
const
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
326
|
+
const ref = input.ref;
|
|
327
|
+
const previousLockEntry = yield* ws
|
|
328
|
+
.getLockedSkill(ref.skill.name)
|
|
329
|
+
.pipe(Effect.catch(() => Effect.succeed(Option.none())));
|
|
330
|
+
const previousVersion = Option.match(previousLockEntry, {
|
|
331
|
+
onNone: () => undefined,
|
|
332
|
+
onSome: previousResolvedVersion,
|
|
333
|
+
});
|
|
334
|
+
const { skillSrcPath } = yield* ws.getSkillDir(ref.skill.name, skillPathSourceFor(ref));
|
|
335
|
+
const sourceHashBeforeInstall = Option.match(previousLockEntry, {
|
|
336
|
+
onNone: () => undefined,
|
|
337
|
+
onSome: previousSourceHash,
|
|
338
|
+
}) ??
|
|
339
|
+
(yield* Effect.gen(function* () {
|
|
340
|
+
const exists = yield* fs
|
|
341
|
+
.exists(skillSrcPath)
|
|
342
|
+
.pipe(Effect.catch(() => Effect.succeed(false)));
|
|
343
|
+
if (!exists)
|
|
344
|
+
return undefined;
|
|
345
|
+
return yield* computeSkillSourceHash(skillSrcPath);
|
|
346
|
+
}));
|
|
347
|
+
const configuredAgents = yield* agentRepo.getMaterializationAgents();
|
|
348
|
+
const resolvedAgents = yield* Effect.forEach(configuredAgents, (agent) => agent
|
|
349
|
+
.resolveEffectiveSkillsDir({ workspaceRoot: ws.baseDir })
|
|
350
|
+
.pipe(Effect.map((outcome) => ({ agentId: agent.id, outcome }))), { concurrency: "unbounded" });
|
|
351
|
+
const unknownAgents = yield* agentRepo.getUnknownConfiguredAgentIds();
|
|
352
|
+
const skippedAgents = resolvedAgents.flatMap(({ agentId, outcome }) => outcome._tag === "unsupported" || outcome._tag === "disabled"
|
|
353
|
+
? [`${agentId}: ${outcome.reason}`]
|
|
354
|
+
: []);
|
|
355
|
+
const warnings = [
|
|
356
|
+
...(unknownAgents.length === 0
|
|
357
|
+
? []
|
|
358
|
+
: [`Skipping unknown configured agents: ${unknownAgents.join(", ")}`]),
|
|
359
|
+
...(skippedAgents.length === 0
|
|
360
|
+
? []
|
|
361
|
+
: [`Skipping non-installable configured agents: ${skippedAgents.join(", ")}`]),
|
|
362
|
+
];
|
|
363
|
+
const sanitizedName = sanitizeName(ref.skill.name);
|
|
364
|
+
const installableTargets = resolvedAgents.flatMap(({ agentId, outcome }) => outcome._tag === "supported" ? [{ agentId, targetDir: path.normalize(outcome.dir) }] : []);
|
|
365
|
+
const targetLocations = yield* groupInstallTargetsByDirectory(installableTargets, ws.baseDir);
|
|
366
|
+
const artifactAgents = artifactAgentIdsFromTargets(installableTargets);
|
|
367
|
+
const targets = yield* Effect.forEach(targetLocations, (location) => {
|
|
368
|
+
const linkPath = path.join(location.targetDir, sanitizedName);
|
|
369
|
+
return targetChangeBeforeInstall({
|
|
370
|
+
linkPath,
|
|
371
|
+
canonicalSkillSrcPath: skillSrcPath,
|
|
372
|
+
}).pipe(Effect.map((change) => {
|
|
373
|
+
const agentIds = artifactTargetAgentIds(location.agentIds);
|
|
374
|
+
return {
|
|
375
|
+
path: path.relative(ws.baseDir, linkPath),
|
|
376
|
+
change,
|
|
377
|
+
...(agentIds.length > 0 ? { agentIds } : {}),
|
|
378
|
+
};
|
|
379
|
+
}));
|
|
380
|
+
}, { concurrency: "unbounded" });
|
|
381
|
+
const firstTarget = targets[0];
|
|
382
|
+
const rawDisplayPath = firstTarget === undefined ? path.relative(ws.baseDir, skillSrcPath) : firstTarget.path;
|
|
383
|
+
const version = ref.refType === "registry" ? ref.version : undefined;
|
|
384
|
+
const installedBefore = yield* skillManager
|
|
385
|
+
.isInstalled({ target: { type: "skill", name: ref.skill.name } })
|
|
386
|
+
.pipe(Effect.catch(() => Effect.succeed(false)));
|
|
387
|
+
const releaseAgeWarning = input.operation === "install"
|
|
388
|
+
? yield* brandNewReleaseAgeWarning(ref, installedBefore)
|
|
389
|
+
: Option.none();
|
|
390
|
+
const step = withPlanWarning(buildInstallOperation(skillManager, {
|
|
391
|
+
toStepFailure: lifecycleStepFailure,
|
|
392
|
+
ref,
|
|
393
|
+
...(input.operation === "install"
|
|
394
|
+
? { declaration: { name: ref.skill.name, versionRange: input.versionRange } }
|
|
395
|
+
: {}),
|
|
396
|
+
force: input.force,
|
|
397
|
+
installedBefore: Effect.succeed(installedBefore),
|
|
398
|
+
buildArtifact: ({ installedBefore }) => Effect.gen(function* () {
|
|
399
|
+
const fileCount = yield* countFiles(fs, path, skillSrcPath);
|
|
400
|
+
const currentSourceHash = yield* computeSkillSourceHash(skillSrcPath);
|
|
401
|
+
const sameVersion = previousVersion === version;
|
|
402
|
+
const sameSource = sourceHashBeforeInstall === currentSourceHash;
|
|
403
|
+
const fallbackChange = !installedBefore
|
|
404
|
+
? "created"
|
|
405
|
+
: sameVersion && sameSource
|
|
406
|
+
? "unchanged"
|
|
407
|
+
: "updated";
|
|
408
|
+
const artifactChange = artifactChangeFromTargets(fallbackChange, targets);
|
|
409
|
+
const sourceDetails = gitHostedSkillArtifactSource(ref);
|
|
410
|
+
return {
|
|
411
|
+
path: rawDisplayPath.length === 0 ? "." : rawDisplayPath,
|
|
412
|
+
scope: ws.scope,
|
|
413
|
+
agents: artifactAgents,
|
|
414
|
+
...(version !== undefined ? { version } : {}),
|
|
415
|
+
change: artifactChange,
|
|
416
|
+
...(previousVersion !== undefined && previousVersion !== version
|
|
417
|
+
? { previousVersion }
|
|
418
|
+
: {}),
|
|
419
|
+
fileCount,
|
|
420
|
+
...(targets.length > 0 ? { targets } : {}),
|
|
421
|
+
...(sourceDetails !== undefined ? { source: sourceDetails } : {}),
|
|
422
|
+
};
|
|
423
|
+
}),
|
|
424
|
+
}), releaseAgeWarning);
|
|
425
|
+
return warnings.length === 0 ? step : withPlanWarning(step, Option.some(warnings.join("; ")));
|
|
426
|
+
}).pipe(Effect.mapError((cause) => installRefused({
|
|
427
|
+
category: "internal",
|
|
428
|
+
detail: `Skill install planning failed for ${input.ref.skill.name}`,
|
|
429
|
+
cause,
|
|
430
|
+
})), Effect.withSpan("SkillInstallation.planStep"));
|
|
431
|
+
/** The closures a settled skill intent becomes. */
|
|
432
|
+
export const planSkillInstall = Effect.fn("InstallExtensions.planSkills")(function* (intent) {
|
|
433
|
+
const steps = yield* Effect.forEach(intent.skillsToInstall, (entry) => planSkillInstallationStep({ ...entry, operation: "install", force: intent.force === true }), { concurrency: 1 });
|
|
417
434
|
return {
|
|
418
435
|
_tag: "Plan",
|
|
419
436
|
name: intent.skillsToInstall.length === 0
|
|
@@ -12,14 +12,16 @@ import * as Effect from "effect/Effect";
|
|
|
12
12
|
import * as FileSystem from "effect/FileSystem";
|
|
13
13
|
import * as Option from "effect/Option";
|
|
14
14
|
import * as Path from "effect/Path";
|
|
15
|
-
import { SkillManager,
|
|
15
|
+
import { SkillManager, skillArtifactFromTargets, } from "@agentxm/extension-materialization";
|
|
16
|
+
import { buildUninstallOperation } from "@agentxm/workspace-reconciliation";
|
|
16
17
|
import { resolveInstalledIdentifierNameOrInput } from "@agentxm/extension-sources";
|
|
18
|
+
import { parseExtensionFqnParts } from "@agentxm/extension-model/unstable/extensions";
|
|
17
19
|
import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
18
20
|
import { WorkspaceMutations, acquiredExtensionDisplayPathFromLockEntry, installedRowsByName, sanitizeName, } from "@agentxm/workspace-state";
|
|
19
|
-
import { expandGlob } from "
|
|
21
|
+
import { expandGlob } from "@agentxm/extension-model/unstable/extensions/name-patterns";
|
|
20
22
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
21
23
|
import { installRefused, } from "../../install/vocabulary.js";
|
|
22
|
-
import { makeWorkspaceRetentionPolicy } from "
|
|
24
|
+
import { makeWorkspaceRetentionPolicy } from "@agentxm/workspace-reconciliation";
|
|
23
25
|
import { workspaceAuthoredPath, workspaceCanonicalPath, workspaceCanonicalRoot, workspaceLockfilePath, workspaceSettingsPath, } from "../../workspace-paths.js";
|
|
24
26
|
const skillSourceTarget = (ws, path, configuredSource, lockEntry, sanitizedName) => {
|
|
25
27
|
if (Option.isSome(configuredSource) && configuredSource.value === "workspace") {
|
|
@@ -47,13 +49,19 @@ export const parseSkillUninstallRequest = Effect.fn("UninstallExtensions.parseSk
|
|
|
47
49
|
if (selector.includes("*") && matched.length === 0) {
|
|
48
50
|
return { targets: [] };
|
|
49
51
|
}
|
|
52
|
+
// An identifier nothing installed answers to still names one skill: its
|
|
53
|
+
// extension name, never the fully qualified spelling as a name.
|
|
54
|
+
const unresolvedName = (input) => {
|
|
55
|
+
const fqn = parseExtensionFqnParts(input);
|
|
56
|
+
return fqn?.type === "skill" ? fqn.name : input;
|
|
57
|
+
};
|
|
50
58
|
const names = matched.length > 0
|
|
51
59
|
? matched
|
|
52
60
|
: [
|
|
53
61
|
yield* resolveInstalledIdentifierNameOrInput({
|
|
54
62
|
input: selector,
|
|
55
63
|
resourceType: "skill",
|
|
56
|
-
}).pipe(Effect.mapError((cause) => installRefused({
|
|
64
|
+
}).pipe(Effect.map((name) => (name === selector.trim() ? unresolvedName(name) : name)), Effect.mapError((cause) => installRefused({
|
|
57
65
|
category: "not_found",
|
|
58
66
|
detail: `Skill "${selector}" could not be resolved`,
|
|
59
67
|
cause,
|
|
@@ -69,7 +77,7 @@ export const planSkillUninstall = Effect.fn("UninstallExtensions.planSkills")(fu
|
|
|
69
77
|
const skillManager = yield* SkillManager;
|
|
70
78
|
const agentRepo = yield* CodingAgentRepository;
|
|
71
79
|
const path = yield* Path.Path;
|
|
72
|
-
const retentionPolicy = makeWorkspaceRetentionPolicy(ws);
|
|
80
|
+
const retentionPolicy = makeWorkspaceRetentionPolicy(ws, lifecycleStepFailure);
|
|
73
81
|
const configuredAgents = yield* agentRepo.getMaterializationAgents().pipe(Effect.mapError((cause) => installRefused({
|
|
74
82
|
category: "internal",
|
|
75
83
|
detail: "Configured agents could not be read",
|
package/dist/src/step-failure.js
CHANGED
|
@@ -85,6 +85,18 @@ const carriedFailure = (failure) => {
|
|
|
85
85
|
export const lifecycleStepFailure = (failure) => {
|
|
86
86
|
if (failure instanceof StepFailure)
|
|
87
87
|
return failure;
|
|
88
|
+
if (failure._tag === "McpSharedTargetConflict") {
|
|
89
|
+
return new StepFailure({
|
|
90
|
+
category: "conflict",
|
|
91
|
+
detail: failure.reason,
|
|
92
|
+
suggestions: [
|
|
93
|
+
{
|
|
94
|
+
description: "Use an MCP package whose transport and symbolic inputs are supported by every configured reader of the shared target.",
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
cause: failure,
|
|
98
|
+
});
|
|
99
|
+
}
|
|
88
100
|
if (failure instanceof ExtensionLifecycleFailed) {
|
|
89
101
|
return new StepFailure({
|
|
90
102
|
category: failure.category,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagents selection application contract and workflow.
|
|
3
|
+
* @experimental This API is unstable and may change without notice.
|
|
4
|
+
*/
|
|
5
|
+
import * as Array from "effect/Array";
|
|
6
|
+
import * as Context from "effect/Context";
|
|
7
|
+
import * as Effect from "effect/Effect";
|
|
8
|
+
import type { SubagentExtensionRef } from "@agentxm/extension-model/unstable/extensions/refs/subagent";
|
|
9
|
+
import { type SubagentSelectionRequest } from "../domain/selection.js";
|
|
10
|
+
declare const SubagentSelectionNotFound_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
11
|
+
readonly _tag: "SubagentSelectionNotFound";
|
|
12
|
+
} & Readonly<A>;
|
|
13
|
+
export declare class SubagentSelectionNotFound extends SubagentSelectionNotFound_base<{
|
|
14
|
+
readonly requested: ReadonlyArray<string>;
|
|
15
|
+
readonly available: ReadonlyArray<string>;
|
|
16
|
+
}> {
|
|
17
|
+
}
|
|
18
|
+
declare const SubagentSelectionCancelled_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
19
|
+
readonly _tag: "SubagentSelectionCancelled";
|
|
20
|
+
} & Readonly<A>;
|
|
21
|
+
export declare class SubagentSelectionCancelled extends SubagentSelectionCancelled_base<{
|
|
22
|
+
readonly message: string;
|
|
23
|
+
}> {
|
|
24
|
+
}
|
|
25
|
+
declare const SubagentSelectionUnavailable_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
26
|
+
readonly _tag: "SubagentSelectionUnavailable";
|
|
27
|
+
} & Readonly<A>;
|
|
28
|
+
/** The interface could not obtain a choice; this is distinct from cancellation. */
|
|
29
|
+
export declare class SubagentSelectionUnavailable extends SubagentSelectionUnavailable_base<{
|
|
30
|
+
readonly cause?: unknown;
|
|
31
|
+
}> {
|
|
32
|
+
}
|
|
33
|
+
declare const SubagentSelectionInteraction_base: Context.ServiceClass<SubagentSelectionInteraction, "@agentxm/extension-lifecycle/subagents/SubagentSelectionInteraction", {
|
|
34
|
+
readonly select: (candidates: Array.NonEmptyReadonlyArray<SubagentExtensionRef>) => Effect.Effect<ReadonlyArray<SubagentExtensionRef>, SubagentSelectionCancelled | SubagentSelectionUnavailable>;
|
|
35
|
+
}>;
|
|
36
|
+
export declare class SubagentSelectionInteraction extends SubagentSelectionInteraction_base {
|
|
37
|
+
}
|
|
38
|
+
export type SubagentSelectionFailure = SubagentSelectionNotFound | SubagentSelectionCancelled | SubagentSelectionUnavailable;
|
|
39
|
+
/** Resolve the subagent-owned decision through this capability's interaction port. */
|
|
40
|
+
export declare const determineSubagentsToInstall: (candidates: readonly [SubagentExtensionRef, ...SubagentExtensionRef[]], request: SubagentSelectionRequest) => Effect.Effect<readonly SubagentExtensionRef[], SubagentSelectionFailure, SubagentSelectionInteraction>;
|
|
41
|
+
export {};
|
|
42
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Subagents selection application contract and workflow.
|
|
3
|
+
* @experimental This API is unstable and may change without notice.
|
|
4
|
+
*/
|
|
5
|
+
import * as Array from "effect/Array";
|
|
6
|
+
import * as Context from "effect/Context";
|
|
7
|
+
import * as Data from "effect/Data";
|
|
8
|
+
import * as Effect from "effect/Effect";
|
|
9
|
+
import { decideSubagentSelection } from "../domain/selection.js";
|
|
10
|
+
export class SubagentSelectionNotFound extends Data.TaggedError("SubagentSelectionNotFound") {
|
|
11
|
+
}
|
|
12
|
+
export class SubagentSelectionCancelled extends Data.TaggedError("SubagentSelectionCancelled") {
|
|
13
|
+
}
|
|
14
|
+
/** The interface could not obtain a choice; this is distinct from cancellation. */
|
|
15
|
+
export class SubagentSelectionUnavailable extends Data.TaggedError("SubagentSelectionUnavailable") {
|
|
16
|
+
}
|
|
17
|
+
export class SubagentSelectionInteraction extends Context.Service()("@agentxm/extension-lifecycle/subagents/SubagentSelectionInteraction") {
|
|
18
|
+
}
|
|
19
|
+
/** Resolve the subagent-owned decision through this capability's interaction port. */
|
|
20
|
+
export const determineSubagentsToInstall = Effect.fn("Subagents.determineSelection")(function* (candidates, request) {
|
|
21
|
+
const names = Array.map(candidates, (ref) => ref.subagent.name);
|
|
22
|
+
const decision = decideSubagentSelection(names, request);
|
|
23
|
+
switch (decision.kind) {
|
|
24
|
+
case "selected":
|
|
25
|
+
return candidates.filter((ref) => decision.names.includes(ref.subagent.name));
|
|
26
|
+
case "unmatched":
|
|
27
|
+
return yield* new SubagentSelectionNotFound({
|
|
28
|
+
requested: request.requestedSubagents,
|
|
29
|
+
available: names,
|
|
30
|
+
});
|
|
31
|
+
case "choice-required": {
|
|
32
|
+
const interaction = yield* SubagentSelectionInteraction;
|
|
33
|
+
return yield* interaction.select(candidates);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type * as Array from "effect/Array";
|
|
2
|
+
export interface SubagentSelectionRequest {
|
|
3
|
+
readonly requestedSubagents: ReadonlyArray<string>;
|
|
4
|
+
readonly all: boolean;
|
|
5
|
+
readonly nonInteractive: boolean;
|
|
6
|
+
}
|
|
7
|
+
export type SubagentSelectionDecision = {
|
|
8
|
+
readonly kind: "selected";
|
|
9
|
+
readonly names: ReadonlyArray<string>;
|
|
10
|
+
} | {
|
|
11
|
+
readonly kind: "choice-required";
|
|
12
|
+
} | {
|
|
13
|
+
readonly kind: "unmatched";
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Select this request's subagents, preserving source order.
|
|
17
|
+
* Named patterns take precedence over all/unattended selection. The existing
|
|
18
|
+
* union-of-matches behavior is retained; unresolved selector requirements remain
|
|
19
|
+
* explicit in the installation specification.
|
|
20
|
+
*/
|
|
21
|
+
export declare const decideSubagentSelection: (names: Array.NonEmptyReadonlyArray<string>, request: SubagentSelectionRequest) => SubagentSelectionDecision;
|
|
22
|
+
//# sourceMappingURL=selection.d.ts.map
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { expandGlobs } from "@agentxm/extension-model/unstable/extensions/name-patterns";
|
|
2
|
+
/**
|
|
3
|
+
* Select this request's subagents, preserving source order.
|
|
4
|
+
* Named patterns take precedence over all/unattended selection. The existing
|
|
5
|
+
* union-of-matches behavior is retained; unresolved selector requirements remain
|
|
6
|
+
* explicit in the installation specification.
|
|
7
|
+
*/
|
|
8
|
+
export const decideSubagentSelection = (names, request) => {
|
|
9
|
+
if (request.requestedSubagents.length > 0) {
|
|
10
|
+
const matched = expandGlobs(request.requestedSubagents, names);
|
|
11
|
+
return matched.length === 0 ? { kind: "unmatched" } : { kind: "selected", names: matched };
|
|
12
|
+
}
|
|
13
|
+
if (request.all || request.nonInteractive || names.length === 1) {
|
|
14
|
+
return { kind: "selected", names };
|
|
15
|
+
}
|
|
16
|
+
return { kind: "choice-required" };
|
|
17
|
+
};
|
|
18
|
+
//# sourceMappingURL=selection.js.map
|
|
@@ -18,8 +18,8 @@ import { type SourceResolutionFailure } from "@agentxm/extension-sources";
|
|
|
18
18
|
import { type Plan } from "@agentxm/workspace-operations";
|
|
19
19
|
import type { ExtensionLifecycleFailed } from "../../errors.js";
|
|
20
20
|
import { type RegistryLookupProbe } from "../../install/registry-source-resolution.js";
|
|
21
|
-
import { ExtensionSelectionInteraction, type ExtensionSelectionCancelled } from "../../install/selection-interaction.js";
|
|
22
21
|
import { type InstallStepRequirements, type ResolveInstallRequirements, type SubagentInstallIntent } from "../../install/vocabulary.js";
|
|
22
|
+
import { SubagentSelectionInteraction, type SubagentSelectionFailure } from "../application/index.js";
|
|
23
23
|
/** A subagent source after grammar parsing, before anything is discovered. */
|
|
24
24
|
export interface ParsedSubagentInstallRequest {
|
|
25
25
|
readonly source: Source;
|
|
@@ -43,7 +43,7 @@ export declare const parseSubagentInstallRequest: (args: SubagentInstallArgs) =>
|
|
|
43
43
|
/** Discover the subagents the parsed source offers, or refuse when it has none. */
|
|
44
44
|
export declare const discoverSubagentRefs: (request: ParsedSubagentInstallRequest) => Effect.Effect<ReadonlyArray<SubagentExtensionRef>, ExtensionLifecycleFailed, ResolveInstallRequirements>;
|
|
45
45
|
/** Settle which of the discovered subagents this request installs. */
|
|
46
|
-
export declare const finalizeSubagentInstallIntent: (request: ParsedSubagentInstallRequest, discovered: ReadonlyArray<SubagentExtensionRef>) => Effect.Effect<SubagentInstallIntent, ExtensionLifecycleFailed |
|
|
46
|
+
export declare const finalizeSubagentInstallIntent: (request: ParsedSubagentInstallRequest, discovered: ReadonlyArray<SubagentExtensionRef>) => Effect.Effect<SubagentInstallIntent, ExtensionLifecycleFailed | SubagentSelectionFailure, SubagentSelectionInteraction>;
|
|
47
47
|
/** The closures a settled subagent intent becomes. */
|
|
48
48
|
export declare const planSubagentInstall: (intent: SubagentInstallIntent) => Effect.Effect<Plan<InstallStepRequirements>, ExtensionLifecycleFailed, InstallStepRequirements | SubagentManager>;
|
|
49
49
|
//# sourceMappingURL=plan.d.ts.map
|
|
@@ -10,7 +10,8 @@
|
|
|
10
10
|
import * as Array from "effect/Array";
|
|
11
11
|
import * as Effect from "effect/Effect";
|
|
12
12
|
import * as Option from "effect/Option";
|
|
13
|
-
import { NO_MATERIALIZATION_OBSERVATION, SubagentManager,
|
|
13
|
+
import { NO_MATERIALIZATION_OBSERVATION, SubagentManager, } from "@agentxm/extension-materialization";
|
|
14
|
+
import { buildInstallOperation } from "@agentxm/workspace-reconciliation";
|
|
14
15
|
import { parseInputPattern, } from "@agentxm/extension-model/unstable/sources/parser";
|
|
15
16
|
import { SourceHostProviders, sourceResolutionFailureCategory, } from "@agentxm/extension-sources";
|
|
16
17
|
import { operationPresentation, } from "@agentxm/workspace-operations";
|
|
@@ -18,9 +19,8 @@ import { CodingAgentRepository } from "@agentxm/workspace-projection";
|
|
|
18
19
|
import { WorkspaceMutations } from "@agentxm/workspace-state";
|
|
19
20
|
import { lifecycleStepFailure } from "../../step-failure.js";
|
|
20
21
|
import { sourceFailureDetail, } from "../../install/registry-source-resolution.js";
|
|
21
|
-
import { ExtensionSelectionInteraction, } from "../../install/selection-interaction.js";
|
|
22
22
|
import { installRefused, } from "../../install/vocabulary.js";
|
|
23
|
-
import { determineSubagentsToInstall } from "
|
|
23
|
+
import { determineSubagentsToInstall, SubagentSelectionInteraction, } from "../application/index.js";
|
|
24
24
|
import { resolveSubagentInstallSource } from "./source.js";
|
|
25
25
|
const isRemoteReadNotImplemented = (error) => sourceFailureDetail(error).includes("not implemented");
|
|
26
26
|
const discoverHowToFix = (source, error) => {
|
|
@@ -216,7 +216,7 @@ export const planSubagentInstall = Effect.fn("InstallExtensions.planSubagents")(
|
|
|
216
216
|
return buildInstallOperation(subagentManager, {
|
|
217
217
|
toStepFailure: lifecycleStepFailure,
|
|
218
218
|
ref,
|
|
219
|
-
versionRange: entry.versionRange,
|
|
219
|
+
declaration: { name: ref.subagent.name, versionRange: entry.versionRange },
|
|
220
220
|
installedBefore: subagentManager
|
|
221
221
|
.isInstalled({ target: { type: "subagent", name: ref.subagent.name } })
|
|
222
222
|
.pipe(Effect.catch(() => Effect.succeed(false))),
|
|
@@ -64,9 +64,7 @@ export const enableSubagent = (op) => Effect.gen(function* () {
|
|
|
64
64
|
});
|
|
65
65
|
}
|
|
66
66
|
const baseDir = ws.baseDir;
|
|
67
|
-
const subagentSrcPath =
|
|
68
|
-
? path.join(canonical.value.observation.path, "src")
|
|
69
|
-
: canonical.value.observation.path;
|
|
67
|
+
const subagentSrcPath = path.join(canonical.value.observation.path, "src");
|
|
70
68
|
// Read and parse the subagent content file
|
|
71
69
|
const expectedFilename = subagentContentFilename(op.args.subagentName);
|
|
72
70
|
const contentPath = subagentContentPath(path.join, subagentSrcPath, op.args.subagentName);
|